js/components/table.js
- // NPM IMPORTS
- import assert from 'assert'
-
- // COMMON IMPORTS
- import T from '../../../node_modules/devapt-core-common/dist/js/utils/types'
- import html_entities from '../../../node_modules/devapt-core-common/dist/js/utils/html_entities'
-
- // BROWSER IMPORTS
- import Container from '../base/container'
-
-
- const context = 'browser/components/table'
-
-
-
- /**
- * @file UI component class.
- * @author Luc BORIES
- * @license Apache-2.0
- */
- export default class Table extends Container
- {
-
- /**
- * Creates an instance of Table.
- *
- * @param {object} arg_runtime - client runtime.
- * @param {object} arg_state - component state.
- * @param {string} arg_log_context - context of traces of this instance (optional).
- *
- * API:
- * ->get_children_component():array - Get view children components.
- *
- * ->ui_items_get_count():integer - Get container items count.
- *
- * ->ui_items_append(arg_items_array, arg_items_count):nothing - Append tems to the container.
- * ->ui_items_prepend(arg_items_array, arg_items_count):nothing - Prepend tems to the container.
- * ->ui_items_insert_at(arg_index, arg_items_array, arg_items_count):nothing - Insert items at container position index.
- * ->ui_items_replace(arg_items_array, arg_items_count):nothing - Replace container items.
- *
- * ->ui_items_remove_at_index(arg_index):nothing - Remove a row at given position.
- * ->ui_items_remove_first():nothing - Remove a row at first position.
- * ->ui_items_remove_last(arg_count):nothing - Remove a row at last position.
- *
- * ->build_row(arg_row_array, arg_row_index, arg_max_cols):string - Build a table row html tag.
- * ->update_rows(arg_rows_array, arg_options):nothing - Append or prepend a row.
- * ->update_section_collection(arg_collection_def, arg_collection_values):nothing - Update values on a table part.
- *
- * @returns {nothing}
- */
- constructor(arg_runtime, arg_state, arg_log_context)
- {
- const log_context = arg_log_context ? arg_log_context : context
- super(arg_runtime, arg_state, log_context)
-
- this.is_table_component = true
-
- // DEBUG
- // this.enable_trace()
- }
-
-
-
- /**
- * Get view children components.
- *
- * @returns {array} - list of Component.
- */
- get_children_component()
- {
- if ( ! this._children_component)
- {
- this._children_component = []
-
- const items = this.get_state_value('items', [])
- const headers = this.get_state_value('headers', [])
- const footers = this.get_state_value('headers', [])
- // console.log(context + ':get_children_component:init with items:', items)
-
- headers.forEach(
- (row)=>{
- if ( T.isArray(row) )
- {
- row.forEach(
- (cell)=>{
- if ( T.isObject(cell) )
- {
- if (cell.is_component)
- {
- this._children_component.push(cell)
- return
- }
-
- if ( T.isString(cell.view) )
- {
- const component = window.devapt().ui(cell.view)
- if (component && component.is_component)
- {
- this._children_component.push(component)
- }
- }
- }
- }
- )
-
- }
- }
- )
-
- footers.forEach(
- (row)=>{
- if ( T.isArray(row) )
- {
- row.forEach(
- (cell)=>{
- if ( T.isObject(cell) )
- {
- if (cell.is_component)
- {
- this._children_component.push(cell)
- return
- }
-
- if ( T.isString(cell.view) )
- {
- const component = window.devapt().ui(cell.view)
- if (component && component.is_component)
- {
- this._children_component.push(component)
- }
- }
- }
- }
- )
-
- }
- }
- )
-
- items.forEach(
- (row)=>{
- if ( T.isArray(row) )
- {
- row.forEach(
- (cell)=>{
- if ( T.isObject(cell) )
- {
- if (cell.is_component)
- {
- this._children_component.push(cell)
- return
- }
- if ( T.isString(cell.key) )
- {
- if ( T.isString(cell.view) )
- {
- const component = window.devapt().ui(cell.view)
- if (component && component.is_component)
- {
- this._children_component.push(component)
- }
- }
- }
- }
- }
- )
-
- }
- }
- )
- }
-
- return this._children_component
- }
-
-
-
- /**
- * Get container items count.
- *
- * @returns {nothing}
- */
- ui_items_get_count()
- {
- const table_body_elem = document.getElementById(this.get_dom_id())
- const tr_elems = table_body_elem.children
- return tr_elems.length
- }
-
-
-
- /**
- * Erase container items.
- *
- * @returns {nothing}
- */
- ui_items_clear()
- {
- const table_elem = this.get_dom_element()
- const table_body_elem = table_elem.getElementsByTagName( "tbody" )[0]
-
- while(table_body_elem.hasChildNodes())
- {
- const tr_elem = table_body_elem.lastChild
- this.delete_row_elem(tr_elem)
- table_body_elem.removeChild(tr_elem)
- }
- }
-
-
-
- /**
- * Append items to the container.
- *
- * @param {array} arg_items_array - items array.
- * @param {intege} arg_items_count - items count.
- *
- * @returns {nothing}
- */
- ui_items_append(arg_items_array, arg_items_count)
- {
- // console.log(context + ':ui_items_append:arg_items_array', arg_items_array, arg_items_count)
-
- let arg_options = arg_options ? arg_options : {}
- arg_options.mode = 'append'
- this.update_rows(arg_items_array, arg_options)
- }
-
-
-
- /**
- * Prepend items to the container.
- *
- * @param {array} arg_items_array - items array.
- * @param {intege} arg_items_count - items count.
- *
- * @returns {nothing}
- */
- ui_items_prepend(arg_items_array, arg_items_count)
- {
- // console.log(context + ':ui_items_prepend:%s:count=%s:arg_items_array', this.get_name(), arg_items_count, arg_items_array)
-
- let arg_options = arg_options ? arg_options : {}
- arg_options.mode = 'prepend'
- this.update_rows(arg_items_array, arg_options)
- }
-
-
-
- /**
- * Replace container items.
- *
- * @param {array} arg_items_array - items array.
- * @param {intege} arg_items_count - items count.
- *
- * @returns {nothing}
- */
- ui_items_replace(arg_items_array/*, arg_items_count*/)
- {
- // console.log(context + ':ui_items_replace:arg_items_array', arg_items_array.length)
-
- // REMOVE ALL EXISTING ROWS
- this.ui_items_clear()
-
- let arg_options = arg_options ? arg_options : {}
- arg_options.mode = 'replace'
- this.update_rows(arg_items_array, arg_options)
- }
-
-
-
- /**
- * Insert items at container position index.
- *
- * @param {intege} arg_index - position index.
- * @param {array} arg_items_array - items array.
- * @param {intege} arg_items_count - items count.
- *
- * @returns {nothing}
- */
- ui_items_insert_at(arg_index, arg_items_array, arg_items_count)
- {
- assert( T.isArray(arg_items_array), context + ':ui_items_replace:bad items array')
- assert( T.isNumber(arg_items_count), context + ':ui_items_replace:bad items count')
-
- // NOT YET IMPLEMENTED
- }
-
-
-
- /**
- * Remove a row at given position.
- *
- * @param {number} arg_index - row index.
- *
- * @returns {nothing}
- */
- ui_items_remove_at_index(arg_index)
- {
- assert( T.isNumber(arg_index), context + ':ui_items_remove_at_index:bad index number')
-
- const table_elem = this.get_dom_element()
- const table_body_elem = table_elem.getElementsByTagName( "tbody" )[0]
- if (arg_index < 0 || arg_index >= table_body_elem.children.length)
- {
- console.warn(context + ':ui_items_remove_at_index:%s:bad item index=%s', this.get_name(), arg_index)
- return
- }
- const tr_elem = table_body_elem.children[arg_index]
- this.delete_row_elem(tr_elem)
- table_body_elem.removeChild(tr_elem)
- }
-
-
-
- /**
- * Remove a row at first position.
- *
- * @returns {nothing}
- */
- ui_items_remove_first()
- {
- const table_elem = this.get_dom_element()
- const table_body_elem = table_elem.getElementsByTagName( "tbody" )[0]
- const tr_elem = table_body_elem.firstElementChild()
- this.delete_row_elem(tr_elem)
- table_body_elem.removeChild(tr_elem)
- }
-
-
-
- /**
- * Remove a row at last position.
- *
- * @param {integer} arg_count - items count to remove.
- *
- * @returns {nothing}
- */
- ui_items_remove_last(arg_count=1)
- {
- // console.log(context + ':ui_items_remove_last:arg_count', arg_count)
-
- if (arg_count <= 0)
- {
- return
- }
-
- const table_elem = this.get_dom_element()
- const table_body_elem = table_elem.getElementsByTagName( "tbody" )[0]
- const tr_elems = table_body_elem.children
- const last_index = tr_elems.length - arg_count - 1
- let row_index = last_index - 1 >= 0 ? last_index - 1 : 0
- for( ; row_index < tr_elems.length ; row_index++)
- {
- const tr_elem = tr_elems[row_index]
- this.delete_row_elem(tr_elem)
- table_body_elem.removeChild(tr_elem)
- }
- }
-
-
-
- /**
- * Delete table rows DOM elements.
- *
- * @param {array} arg_rows_array - rows Element array.
- *
- * @returns {Element} - TD DOM Element.
- */
- delete_row_elem(arg_row_elem)
- {
- }
-
-
-
- /**
- * Build a row cell DOM element.
- *
- * @param {any} arg_cell_value - cell value.
- * @param {integer} arg_row_index - row index.
- * @param {integer} arg_column_index - column index.
- * @param {Document} arg_document - DOM document.
- *
- * @returns {Element} - TD DOM Element.
- */
- build_cell(arg_cell_value, arg_row_index, arg_column_index, arg_document)
- {
- const td_elem = arg_document.createElement('td')
-
- td_elem.setAttribute('data-column-index', arg_column_index)
- td_elem.innerText = arg_cell_value
-
- return td_elem
- }
-
-
-
- /**
- * Build a table row DOM element.
- *
- * @param {array} arg_row_array - row values array.
- * @param {integer} arg_row_index - row index.
- * @param {integer} arg_max_cols - max columns number.
- * @param {integer} arg_depth - path depth.
- *
- * @returns {Element} - TD DOM Element.
- */
- build_row(arg_row_array, arg_row_index, arg_max_cols/*, arg_depth*/)
- {
- const this_document = this.get_dom_element().ownerDocument
- const row_elem = this_document.createElement('tr')
- row_elem.setAttribute('data-row-index', arg_row_index)
-
- // DEBUG
- // console.log(context + ':build_row:rows_index=%i row_array max_cols', arg_row_index, arg_row_array, arg_max_cols)
-
- if( ! T.isArray(arg_row_array) )
- {
- console.warn(context + ':build_row:row_array is not an array at rows_index=%i', arg_row_index, arg_row_array)
- return undefined
- }
-
- arg_row_array.forEach(
- (cell, index) => {
- if (arg_max_cols && index > arg_max_cols)
- {
- return
- }
-
- const td_elem = this.build_cell(cell, arg_row_index, index, this_document)
- if (! td_elem)
- {
- console.warn(context + ':build_row:bad cell element at rows_index=%i at column_index=%i', arg_row_index, index)
- return
- }
-
- row_elem.appendChild(td_elem)
- }
- )
-
- return row_elem
- }
-
-
-
- /**
- * Build a table row DOM element.
- *
- * @param {Element} arg_body_element - table body element.
- * @param {array} arg_row_array - row values array.
- * @param {integer} arg_row_index - row index.
- * @param {integer} arg_max_rows - max rows number.
- * @param {integer} arg_max_cols - max columns number.
- * @param {string} arg_mode - fill mode:append/prepend
- * @param {string} arg_max_rows_action - action on max rows.
- * @param {integer} arg_depth - path depth.
- *
- * @returns {Element} - TD DOM Element.
- */
- process_row_array(arg_body_element, arg_row_array, arg_row_index, arg_max_rows, arg_max_cols, arg_mode, arg_max_rows_action, arg_depth=0)
- {
- const rows_count = arg_body_element.children.length
- const row_elem = this.build_row(arg_row_array, arg_row_index, arg_max_cols, arg_depth)
- if (! row_elem)
- {
- console.warn(context + ':update_rows:%s:at %i:max cols=%i:bad row element for ', this.get_name(), arg_row_index, arg_max_cols, arg_row_array)
- return
- }
-
- if (arg_max_rows && (rows_count + arg_row_index) > arg_max_rows)
- {
- if (arg_max_rows_action == 'remove_bottom')
- {
- // TODO
- console.warn('TODO remove_bottom')
- }
- else if (arg_max_rows_action == 'remove_top')
- {
- // TODO
- console.warn('TODO remove_top')
- }
- else
- {
- return
- }
-
- }
-
- // console.log(context + ':update_rows:rows_index=%i mode=%s', row_index, arg_options.mode)
- if (arg_mode == 'prepend')
- {
- arg_body_element.insertBefore(row_elem, arg_body_element.firstChild )
- }
- else
- {
- arg_body_element.appendChild(row_elem)
- }
- }
-
-
-
- /**
- * Append or prepend a row.
- *
- * @param {array} arg_rows_array - rows array.
- * @param {object} arg_options - operation settigs (optional).
- *
- * @returns {nothing}
- */
- update_rows(arg_rows_array, arg_options)
- {
- const arg_table_id = this.get_dom_id()
- assert( T.isString(arg_table_id), context + ':update_rows:bad table id string:' + arg_table_id)
- assert( T.isArray(arg_rows_array), context + ':update_rows:bad rows array')
-
- const state = this.get_state()
-
- arg_options = arg_options ? arg_options : {}
- arg_options.mode = arg_options.mode ? arg_options.mode : 'append'
-
- const table_elem = this.get_dom_element()
- const table_body_elem = table_elem.getElementsByTagName( "tbody" )[0]
-
- const max_cols = T.isNumber(state.max_columns) ? state.max_columns : undefined
- const max_rows = T.isNumber(state.max_rows) ? state.max_rows : undefined
- const max_rows_action = T.isString(state.max_rows_action) ? state.max_rows_action : undefined
-
- let fields_count = this.get_state_value('fields_count', 0)
- if (fields_count == 0)
- {
- const headers = this.get_state_value('headers', [])
- if ( T.isArray(headers) && headers.length > 0 )
- {
- const last_headers = headers[headers.length - 1]
- if ( T.isArray(last_headers) )
- {
- fields_count = last_headers.length
- }
- }
- }
-
- // DEBUG
- // console.log( context + ':update_rows:arg_rows_array=', arg_rows_array)
- // console.log( context + ':update_rows:rows_count=%i', rows_count)
-
- arg_rows_array.forEach(
- (arg_row_array, row_index) => {
-
- const row_array = T.isArray(arg_row_array) ? arg_row_array : (fields_count == 1 ? [arg_row_array] : undefined)
- if (! row_array)
- {
- console.warn(context + ':update_rows:%s:at %i:fields_count=%i:bad row array for ', this.get_name(), row_index, fields_count, arg_row_array)
- return
- }
-
- this.process_row_array(table_body_elem, row_array, row_index, max_rows, max_cols, arg_options.mode, max_rows_action)
- }
- )
- }
-
-
-
- /**
- * Update values on a table part.
- *
- * @param {object} arg_collection_def - plain object map of collection definition ({collection_name:"", collection_dom_id:""}.
- * @param {object} arg_collection_values - plain object map of collection key/value pairs.
- *
- * @returns {nothing}
- */
- update_section_collection(arg_collection_def, arg_collection_values)
- {
- const table_id = this.get_dom_id()
-
- // console.log(context + ':update_section_collection:%s:def= values=', this.get_name(), arg_collection_def, arg_collection_values)
-
- if (arg_collection_def && arg_collection_def.collection_name && arg_collection_def.collection_dom_id && arg_collection_values)
- {
- const arg_collection_name = arg_collection_def.collection_name
-
- const collection_elem = document.getElementById(arg_collection_def.collection_dom_id)
- if (!collection_elem)
- {
- // CALLED BY BINDING ON A VIEW WHICH IS NOT VISIBLE
- // console.log(context + ':update_section_collection:' + this.get_name() + ':collection element not found for id [' + arg_collection_def.collection_dom_id + ']')
- return
- }
-
- var collection_dom_template_default = "<tr> <td></td> <td> {collection_key} </td> <td id='{collection_id}'>{collection_value}</td> </tr>"
- var collection_dom_template = arg_collection_def.collection_dom_template ? html_entities.decode(arg_collection_def.collection_dom_template) : collection_dom_template_default
-
- // DEBUG
- // console.log(context + ':update_section_collection:arg_collection_def.collection_dom_template=%s', arg_collection_def.collection_dom_template)
- // console.log(context + ':update_section_collection:collection_dom_template=%s', collection_dom_template)
-
- var collection_key_safe = undefined
- var collection_value = undefined
- var collection_id = undefined
- var collection_value_elem = undefined
- var collection_value_html = undefined
- var collection_keys = Object.keys(arg_collection_values)
- var re = /[^a-zA-Z0-9]/gi
-
- // console.log('update_metric_collection2:collection=%s keys= jqo=', arg_collection_name, collection_keys, arg_collection_jqo)
-
- collection_keys.forEach(
- function(collection_key)
- {
- collection_key_safe = collection_key.replace(re, '_')
- // console.log('update_metric_collection2:collection=%s loop on key=', arg_collection_name, collection_key)
-
- collection_value = arg_collection_values[collection_key]
- collection_id = table_id + "_" + arg_collection_name + "_" + collection_key_safe
- collection_value_elem = document.getElementById(collection_id)
-
- if (! collection_value_elem )
- {
- // console.log('update_metric_collection2:collection=%s loop on key=', collection_key)
-
- collection_value_html = collection_dom_template.replace('{collection_key}', collection_key).replace('{collection_id}', collection_id).replace('{collection_value}', collection_value)
-
- // console.log(context + ':update_section_collection:html', collection_value_html)
-
- const tr_elem = document.createElement('tr')
- tr_elem.innerHTML = collection_value_html
- collection_elem.parentNode.insertBefore(tr_elem, collection_elem.nextSibling)
- collection_value_elem = document.getElementById(collection_id)
- }
-
- collection_value_elem.textContent = collection_value
- }
- )
- }
- }
- }