(function ( wpI18n, wpBlocks, wpElement, wpEditor, wpComponents ) { const { __ } = wpI18n; const { Component, Fragment } = wpElement; const { registerBlockType, createBlock } = wpBlocks; const { InspectorControls, BlockControls, RichText, PanelColorSettings } = wpEditor; const { PanelBody, BaseControl, RangeControl, SelectControl, ToggleControl, TextControl, IconButton, Button, Toolbar, DropdownMenu, Tooltip } = wpComponents; const { times } = lodash; const tableBlockIcon = ( ); let willSetContent = null; let lastValue = ''; class AdvTable extends Component { constructor() { super( ...arguments ); this.state = { initRow: 3, initCol: 3, selectedCell: null, rangeSelected: null, multiSelected: null, sectionSelected: null, updated: false, }; this.calculateRealColIndex = this.calculateRealColIndex.bind( this ); this.isMultiSelected = this.isMultiSelected.bind( this ); this.isRangeSelected = this.isRangeSelected.bind( this ); } componentWillMount() { const { attributes, setAttributes } = this.props; const currentBlockConfig = advgbDefaultConfig['advgb-table']; // No override attributes of blocks inserted before if (attributes.changed !== true) { if (typeof currentBlockConfig === 'object' && currentBlockConfig !== null) { Object.keys(currentBlockConfig).map((attribute) => { if (typeof attributes[attribute] === 'boolean') { attributes[attribute] = !!currentBlockConfig[attribute]; } else { attributes[attribute] = currentBlockConfig[attribute]; } }); } // Finally set changed attribute to true, so we don't modify anything again setAttributes( { changed: true } ); } } componentDidMount() { this.calculateRealColIndex( 'head' ); } componentDidUpdate() { const { isSelected } = this.props; const { selectedCell, updated } = this.state; if ( ! isSelected && selectedCell ) { this.setState( { selectedCell: null, rangeSelected: null, multiSelected: null, } ); } if (updated) { this.calculateRealColIndex(); this.setState( { updated: false } ); } } createTable() { const { setAttributes } = this.props; const { initRow, initCol } = this.state; this.setState( { updated: true } ); return setAttributes( { body: times( parseInt(initRow), () => ( { cells: times( parseInt(initCol), () => ( { content: '', } ) ) } ) ) } ); } // Check if is multi cells selected isMultiSelected() { const { multiSelected } = this.state; return ( multiSelected && multiSelected.length > 1 ); } // Check if is range cells selected isRangeSelected() { const { rangeSelected } = this.state; return (rangeSelected && rangeSelected.toCell); } calculateRealColIndex() { const { attributes, setAttributes } = this.props; [ 'head', 'body', 'foot' ].forEach( ( section ) => { if (!attributes[section].length) return null; const newSection = attributes[section].map( (row, cRow) => { return { cells: row.cells.map( (cell, cCol) => { cell.cI = cCol; for (let i=0;i < cRow; i++) { for (let j=0; j < attributes[section][i].cells.length; j++) { if (attributes[section][i].cells[j] && attributes[section][i].cells[j].colSpan) { if (attributes[section][i].cells[j].rowSpan && i + parseInt(attributes[section][i].cells[j].rowSpan) > cRow) { if (cCol === 0) { if (attributes[section][i].cells[j].cI <= cell.cI) { cell.cI += parseInt( attributes[section][i].cells[j].colSpan ); } } else { const lastColSpan = !isNaN(parseInt(row.cells[cCol-1].colSpan)) ? parseInt(row.cells[cCol-1].colSpan) : 0; if (attributes[section][i].cells[j].cI === row.cells[cCol - 1].cI + 1 || attributes[section][i].cells[j].cI <= row.cells[cCol - 1].cI + lastColSpan ) { cell.cI += parseInt( attributes[section][i].cells[j].colSpan ); } } } } } } for (let j=0; j < cCol; j++) { if (row.cells[j]) { if (row.cells[j].colSpan) { cell.cI += parseInt( row.cells[j].colSpan ) - 1; } } } return cell; } ) } } ); setAttributes( { [section] : newSection } ); } ) } insertRow( offset ) { const { selectedCell, sectionSelected } = this.state; if (!selectedCell) { return null; } const { attributes, setAttributes } = this.props; const currentSection = attributes[ sectionSelected ]; const { rowIndex } = selectedCell; const newRow = jQuery.extend( true, {}, currentSection[rowIndex] ); newRow.cells.map( ( cell ) => { cell.content = ''; return cell; } ); newRow.cells = newRow.cells.filter( ( cCell ) => !cCell.rowSpan ); const newSection = [ ...currentSection.slice( 0, rowIndex + offset ), newRow, ...currentSection.slice( rowIndex + offset ), ].map( ( row, rowIdx ) => ( { cells: row.cells.map( ( cell ) => { if (cell.rowSpan) { if (rowIdx <= rowIndex && ( (rowIdx + parseInt(cell.rowSpan) - 1) >= rowIndex) ) { cell.rowSpan = parseInt(cell.rowSpan) + 1; } } return cell; } ) } ) ); this.setState( { selectedCell: null, sectionSelected: null, updated: true, } ); setAttributes( { [ sectionSelected ]: newSection } ); } deleteRow() { const { selectedCell, sectionSelected } = this.state; if (!selectedCell) { return null; } const { attributes, setAttributes } = this.props; const currentSection = attributes[ sectionSelected ]; const { rowIndex } = selectedCell; const newSection = currentSection.map( (row, cRowIdx) => ( { cells: row.cells.map( (cell) => { if (cell.rowSpan) { if (cRowIdx <= rowIndex && parseInt(cell.rowSpan) + cRowIdx > rowIndex) { cell.rowSpan = parseInt(cell.rowSpan) - 1; if (cRowIdx === rowIndex) { const findColIdx = currentSection[cRowIdx + 1].cells.findIndex( (elm) => elm.cI === cell.cI || elm.cI > cell.cI ); currentSection[cRowIdx + 1].cells.splice( findColIdx, 0, cell ); } } } return cell; } ) } ) ); this.setState( { selectedCell: null, sectionSelected: null, updated: true, } ); if (newSection.length < 2) { alert( __( 'At least 1 row of current section must present.' ) ); return false; } setAttributes( { [ sectionSelected ]: newSection.filter( (row, index) => index !== rowIndex ), } ); } insertColumn( offset ) { const { selectedCell } = this.state; if (!selectedCell) { return null; } const { attributes, setAttributes } = this.props; const { cI } = selectedCell; let countRowSpan = 0; this.setState( { selectedCell: null, updated: true } ); [ 'head', 'body', 'foot' ].forEach( ( section ) => ( setAttributes( { [ section ]: attributes[ section ].map( ( row ) => { if (countRowSpan > 0) { // Skip if previous cell has row span countRowSpan--; return row; } let findColIdx = row.cells.findIndex( (cell, idx) => cell.cI === cI || (row.cells[idx + 1] && row.cells[idx + 1].cI > cI) ); if (findColIdx === -1) { findColIdx = row.cells.length - 1; } if (row.cells[findColIdx].colSpan && row.cells[findColIdx].cI < cI + offset && row.cells[findColIdx].cI + parseInt(row.cells[findColIdx].colSpan) > cI + offset ) { row.cells[findColIdx].colSpan++; if (row.cells[findColIdx].rowSpan) { countRowSpan = parseInt(row.cells[findColIdx].rowSpan) - 1; } return row; } else { let realOffset = offset; if (row.cells[findColIdx].cI > cI && offset === 1) { realOffset = 0; } else if (row.cells[findColIdx].cI < cI && offset === 0) { realOffset = 1; } return { cells: [ ...row.cells.slice( 0, findColIdx + realOffset ), { content: '' }, ...row.cells.slice( findColIdx + realOffset ), ], } } } ), } ) ) ) } deleteColumn() { const { selectedCell } = this.state; if (!selectedCell) { return null; } const { attributes, setAttributes } = this.props; const { cI } = selectedCell; let countRowSpan = 0; this.setState( { selectedCell: null, updated: true } ); [ 'head', 'body', 'foot' ].forEach( ( section ) => ( setAttributes( { [ section ]: attributes[ section ].map( ( row ) => { if (countRowSpan > 0) { countRowSpan--; return row; } const findColIdx = row.cells.findIndex( (cell, idx) => cell.cI === cI || (row.cells[idx + 1] && row.cells[idx + 1].cI > cI) ); if (row.cells[findColIdx].rowSpan) { countRowSpan = parseInt(row.cells[findColIdx].rowSpan) - 1; } if (row.cells[findColIdx].colSpan) { row.cells[findColIdx].colSpan--; if (row.cells[findColIdx].colSpan <= 1) { delete row.cells[findColIdx].colSpan; } return row; } return { cells: row.cells.filter( ( cell, index ) => index !== findColIdx ), } } ), } ) ) ) } mergeCells() { const { rangeSelected, sectionSelected } = this.state; if (!this.isRangeSelected()) { return null; } const { attributes, setAttributes } = this.props; const { fromCell, toCell } = rangeSelected; const currentSection = attributes[ sectionSelected ]; const fCell = currentSection[fromCell.rowIdx].cells[fromCell.colIdx]; const tCell = currentSection[toCell.rowIdx].cells[toCell.colIdx]; const fcSpan = typeof fCell.colSpan === 'undefined' ? 0 : parseInt(fCell.colSpan) - 1; const frSpan = typeof fCell.rowSpan === 'undefined' ? 0 : parseInt(fCell.rowSpan) - 1; const tcSpan = typeof tCell.colSpan === 'undefined' ? 0 : parseInt(tCell.colSpan) - 1; const trSpan = typeof tCell.rowSpan === 'undefined' ? 0 : parseInt(tCell.rowSpan) - 1; const minRowIdx = Math.min(fromCell.rowIdx, toCell.rowIdx); const maxRowIdx = Math.max(fromCell.rowIdx + frSpan, toCell.rowIdx + trSpan); const minColIdx = Math.min(fromCell.RCI, toCell.RCI); const maxColIdx = Math.max(fromCell.RCI + fcSpan, toCell.RCI + tcSpan); const newSection = currentSection.map( ( row, curRowIndex ) => { if (curRowIndex < minRowIdx || curRowIndex > maxRowIdx) { return row; } return { cells: row.cells.map( ( cell, curColIndex ) => { if (curColIndex === Math.min(fromCell.colIdx, toCell.colIdx) && curRowIndex === Math.min(fromCell.rowIdx, toCell.rowIdx) ) { const rowSpan = Math.abs(maxRowIdx - minRowIdx) + 1; const colSpan = Math.abs(maxColIdx - minColIdx) + 1; return { ...cell, rowSpan: rowSpan > 1 ? rowSpan : undefined, colSpan: colSpan > 1 ? colSpan : undefined, } } return cell; } ).filter( (cell, cCol) => cell.cI < minColIdx || ( cCol === Math.min(fromCell.colIdx, toCell.colIdx) && curRowIndex === Math.min(fromCell.rowIdx, toCell.rowIdx) ) || cell.cI > maxColIdx ) } } ); setAttributes( { [ sectionSelected ]: newSection } ); this.setState( { selectedCell: null, sectionSelected: null, rangeSelected: null, updated: true, } ); } splitMergedCells() { const { selectedCell, sectionSelected } = this.state; if (!selectedCell) { return null; } const { attributes, setAttributes } = this.props; const { colIndex, rowIndex, cI } = selectedCell; const cellColSpan = attributes[ sectionSelected ][rowIndex].cells[colIndex].colSpan ? parseInt(attributes[ sectionSelected ][rowIndex].cells[colIndex].colSpan) : 1; const cellRowSpan = attributes[ sectionSelected ][rowIndex].cells[colIndex].rowSpan ? parseInt(attributes[ sectionSelected ][rowIndex].cells[colIndex].rowSpan) : 1; attributes[ sectionSelected ][rowIndex].cells[colIndex].colSpan = undefined; attributes[ sectionSelected ][rowIndex].cells[colIndex].rowSpan = undefined; const newSection = attributes[ sectionSelected ].map( (row, curRowIndex) => { if (curRowIndex >= rowIndex && curRowIndex < (rowIndex + cellRowSpan) ) { const findColIdx = row.cells.findIndex( (cell) => cell.cI >= cI ); let startRowFix = 0; if (curRowIndex === rowIndex) { startRowFix = 1; } return { cells: [ ...row.cells.slice( 0, findColIdx + startRowFix ), ...times( cellColSpan - startRowFix, () => ( { content: '' } ) ), ...row.cells.slice( findColIdx + startRowFix ), ], } } return row; } ); setAttributes( { [ sectionSelected ]: newSection } ); this.setState( { selectedCell: null, sectionSelected: null, updated: true, } ); } // Parse styles from HTML form to React styles object static parseStyles( styles ) { if (typeof styles !== 'string') { return styles; } return styles .split(';') .filter(style => style.split(':')[0] && style.split(':')[1]) .map(style => [ style.split(':')[0].trim().replace(/-./g, c => c.substr(1).toUpperCase()), style.split(':')[1].trim() ]) .reduce((styleObj, style) => ({ ...styleObj, [style[0]]: style[1], }), {}); } getCellStyles( style ) { const { selectedCell, sectionSelected } = this.state; const section = this.props.attributes[ sectionSelected ]; if (!selectedCell) return undefined; const { rowIndex, colIndex } = selectedCell; if (style === 'borderColor') { return section[rowIndex].cells[colIndex].borderColorSaved; } const styles = AdvTable.parseStyles(section[rowIndex].cells[colIndex].styles); if (typeof styles === 'object') { let convertedStyles = styles[style]; if (convertedStyles && typeof convertedStyles !== 'number' && convertedStyles.indexOf( 'px' )) { convertedStyles = styles[style].replace( /px/g, '' ); } return typeof convertedStyles === 'undefined' && style === 'borderStyle' ? 'solid' : convertedStyles; } else { if (typeof styles !== 'undefined') { let convertedStyles = styles[style]; } return typeof convertedStyles === 'undefined' && style === 'borderStyle' ? 'solid' : undefined; } } updateCellsStyles( style ) { const { selectedCell, rangeSelected, multiSelected, sectionSelected } = this.state; if (!selectedCell && !this.isRangeSelected() && !this.isMultiSelected() ) { return null; } const { attributes, setAttributes } = this.props; const { rowIndex, colIndex } = selectedCell; const section = attributes[ sectionSelected ]; let minRowIdx, maxRowIdx, minColIdx, maxColIdx; if (this.isRangeSelected()) { const { fromCell, toCell } = rangeSelected; const fCell = section[fromCell.rowIdx].cells[fromCell.colIdx]; const tCell = section[toCell.rowIdx].cells[toCell.colIdx]; const fcSpan = typeof fCell.colSpan === 'undefined' ? 0 : parseInt(fCell.colSpan) - 1; const frSpan = typeof fCell.rowSpan === 'undefined' ? 0 : parseInt(fCell.rowSpan) - 1; const tcSpan = typeof tCell.colSpan === 'undefined' ? 0 : parseInt(tCell.colSpan) - 1; const trSpan = typeof tCell.rowSpan === 'undefined' ? 0 : parseInt(tCell.rowSpan) - 1; minRowIdx = Math.min(fromCell.rowIdx, toCell.rowIdx); maxRowIdx = Math.max(fromCell.rowIdx + frSpan, toCell.rowIdx + trSpan); minColIdx = Math.min(fromCell.RCI, toCell.RCI); maxColIdx = Math.max(fromCell.RCI + fcSpan, toCell.RCI + tcSpan); } const newSection = section.map( ( row, curRowIndex ) => { if (!this.isRangeSelected() && !this.isMultiSelected() && curRowIndex !== rowIndex || (this.isRangeSelected() && (curRowIndex < minRowIdx || curRowIndex > maxRowIdx) ) || (this.isMultiSelected() && multiSelected.findIndex( (c) => c.rowIndex === curRowIndex ) === -1) ) { return row; } return { cells: row.cells.map( ( cell, curColIndex ) => { if (!this.isRangeSelected() && !this.isMultiSelected() && curColIndex === colIndex || (this.isRangeSelected() && (cell.cI >= minColIdx && cell.cI <= maxColIdx) ) || (this.isMultiSelected() && multiSelected.findIndex( (c) => c.colIndex === curColIndex && c.rowIndex === curRowIndex ) > -1) ) { cell.styles = AdvTable.parseStyles( cell.styles ); if (style.borderColor) { // Set border color if (cell.styles.borderTopColor) { cell.styles = { ...cell.styles, borderTopColor: style.borderColor }; } if (cell.styles.borderRightColor) { cell.styles = { ...cell.styles, borderRightColor: style.borderColor }; } if (cell.styles.borderBottomColor) { cell.styles = { ...cell.styles, borderBottomColor: style.borderColor }; } if (cell.styles.borderLeftColor) { cell.styles = { ...cell.styles, borderLeftColor: style.borderColor }; } cell.borderColorSaved = style.borderColor; } else if (style.setBorder) { // Set border const cellBorderColor = cell.borderColorSaved || '#000'; const cellColSpan = !cell.colSpan ? 0 : parseInt(cell.colSpan) - 1; const cellRowSpan = !cell.rowSpan ? 0 : parseInt(cell.rowSpan) - 1; switch (style.setBorder) { case 'top': cell.styles = { ...cell.styles, borderTopColor: cellBorderColor }; break; case 'right': cell.styles = { ...cell.styles, borderRightColor: cellBorderColor }; break; case 'bottom': cell.styles = { ...cell.styles, borderBottomColor: cellBorderColor }; break; case 'left': cell.styles = { ...cell.styles, borderLeftColor: cellBorderColor }; break; case 'all': cell.styles = { ...cell.styles, borderTopColor: cellBorderColor, borderRightColor: cellBorderColor, borderBottomColor: cellBorderColor, borderLeftColor: cellBorderColor, }; break; case 'none': cell.styles = { ...cell.styles, borderTopColor: undefined, borderRightColor: undefined, borderBottomColor: undefined, borderLeftColor: undefined, }; break; case 'vert': if (cell.cI === minColIdx) { cell.styles = { ...cell.styles, borderRightColor: cellBorderColor, }; } else if (cell.cI + cellColSpan === maxColIdx) { cell.styles = { ...cell.styles, borderLeftColor: cellBorderColor, }; } else { cell.styles = { ...cell.styles, borderRightColor: cellBorderColor, borderLeftColor: cellBorderColor, }; } break; case 'horz': if (curRowIndex === minRowIdx) { cell.styles = { ...cell.styles, borderBottomColor: cellBorderColor, }; } else if (curRowIndex + cellRowSpan === maxRowIdx) { cell.styles = { ...cell.styles, borderTopColor: cellBorderColor, }; } else { cell.styles = { ...cell.styles, borderTopColor: cellBorderColor, borderBottomColor: cellBorderColor, }; } break; case 'inner': if (curRowIndex === minRowIdx) { cell.styles = { ...cell.styles, borderBottomColor: cellBorderColor, }; } else if (curRowIndex + cellRowSpan === maxRowIdx) { cell.styles = { ...cell.styles, borderTopColor: cellBorderColor, }; } else { cell.styles = { ...cell.styles, borderTopColor: cellBorderColor, borderBottomColor: cellBorderColor, }; } if (cell.cI === minColIdx) { cell.styles = { ...cell.styles, borderRightColor: cellBorderColor, }; } else if (cell.cI + cellColSpan === maxColIdx) { cell.styles = { ...cell.styles, borderLeftColor: cellBorderColor, }; } else { cell.styles = { ...cell.styles, borderRightColor: cellBorderColor, borderLeftColor: cellBorderColor, }; } break; case 'outer': if (curRowIndex === minRowIdx) { cell.styles = { ...cell.styles, borderTopColor: cellBorderColor, }; } else if (curRowIndex + cellRowSpan === maxRowIdx) { cell.styles = { ...cell.styles, borderBottomColor: cellBorderColor, }; } if (cell.cI === minColIdx) { cell.styles = { ...cell.styles, borderLeftColor: cellBorderColor, }; } else if (cell.cI + cellColSpan === maxColIdx) { cell.styles = { ...cell.styles, borderRightColor: cellBorderColor, }; } break; default: // Nothing break; } } else { cell.styles = { ...cell.styles, ...style }; } } return cell; } ) } } ); setAttributes( { [ section ]: newSection } ); } updateCellContent( content, cell = null ) { const { selectedCell, sectionSelected } = this.state; if (!selectedCell && !cell) { return null; } let rowIndex, colIndex; if (cell) { rowIndex = cell.rowIndex; colIndex = cell.colIndex; } else { rowIndex = selectedCell.rowIndex; colIndex = selectedCell.colIndex; } const { attributes, setAttributes } = this.props; const newSection = attributes[ sectionSelected ].map( ( row, curRowIndex ) => { if (curRowIndex !== rowIndex) { return row; } return { cells: row.cells.map( ( cell, curColIndex ) => { if (curColIndex !== colIndex) { return cell; } return { ...cell, content, } } ) } } ); setAttributes( { [ sectionSelected ]: newSection } ); } toggleSection( section ) { const { attributes, setAttributes } = this.props; const { sectionSelected } = this.state; const { body } = attributes; const cellsToAdd = [ { cells: body[0].cells.map( (cell) => ( { cI: cell.cI, colSpan: cell.colSpan } ) ) } ]; if (sectionSelected === section) { this.setState( { selectedCell: null, sectionSelected: null, } ) } if (!attributes[section].length) { return setAttributes( { [section] : cellsToAdd } ); } return setAttributes( { [section] : [] } ); } renderSection( section ) { const { attributes } = this.props; const { selectedCell, multiSelected, rangeSelected, sectionSelected } = this.state; return attributes[ section ].map( ( { cells }, rowIndex ) => ( {cells.map( ( { content, styles, colSpan, rowSpan, cI }, colIndex ) => { const cell = { rowIndex, colIndex, cI, section }; let isSelected = selectedCell && selectedCell.rowIndex === rowIndex && selectedCell.colIndex === colIndex && sectionSelected === section; if (this.isRangeSelected()) { const { fromCell, toCell } = rangeSelected; const fCell = attributes[ sectionSelected ][fromCell.rowIdx].cells[fromCell.colIdx]; const tCell = attributes[ sectionSelected ][toCell.rowIdx].cells[toCell.colIdx]; const fcSpan = typeof fCell.colSpan === 'undefined' ? 0 : parseInt(fCell.colSpan) - 1; const frSpan = typeof fCell.rowSpan === 'undefined' ? 0 : parseInt(fCell.rowSpan) - 1; const tcSpan = typeof tCell.colSpan === 'undefined' ? 0 : parseInt(tCell.colSpan) - 1; const trSpan = typeof tCell.rowSpan === 'undefined' ? 0 : parseInt(tCell.rowSpan) - 1; isSelected = rowIndex >= Math.min(fromCell.rowIdx, toCell.rowIdx) && rowIndex <= Math.max(fromCell.rowIdx + frSpan, toCell.rowIdx + trSpan) && cI >= Math.min(fromCell.RCI, toCell.RCI) && cI <= Math.max(fromCell.RCI + fcSpan, toCell.RCI + tcSpan) && section === sectionSelected } if (this.isMultiSelected()) { isSelected = multiSelected.findIndex( (c) => c.rowIndex === rowIndex && c.colIndex === colIndex ) > -1 && multiSelected[0].section === section; } const cellClassName = [ isSelected && 'cell-selected', ].filter( Boolean ).join( ' ' ); styles = AdvTable.parseStyles( styles ); return ( { if (e.shiftKey) { if (!rangeSelected) return; if (!rangeSelected.fromCell) return; const { fromCell } = rangeSelected; if (section !== fromCell.section) { alert( __( 'Cannot select multi cells from difference section!' ) ); return; } const toCell = { rowIdx: rowIndex, colIdx: colIndex, RCI: cI, section: section, }; this.setState( { rangeSelected: { fromCell, toCell }, multiSelected: null, } ); } else if (e.ctrlKey || e.metaKey) { const multiCells = multiSelected ? multiSelected : []; const existCell = multiCells.findIndex( (cel) => cel.rowIndex === rowIndex && cel.colIndex === colIndex ); if (multiCells.length && section !== multiCells[0].section) { alert( __( 'Cannot select multi cells from difference section!' ) ); return; } if (existCell === -1) { multiCells.push(cell); } else { multiCells.splice(existCell, 1); } this.setState( { multiSelected: multiCells, rangeSelected: null, } ); } else { this.setState( { rangeSelected: { fromCell: { rowIdx: rowIndex, colIdx: colIndex, RCI: cI, section: section, }, }, multiSelected: [ cell ], } ); } } } > { if (willSetContent) clearTimeout(willSetContent); lastValue = value; willSetContent = setTimeout( () => this.updateCellContent( value, selectedCell ), 1000); } } unstableOnFocus={ () => { if (willSetContent) { this.updateCellContent(lastValue, selectedCell); clearTimeout(willSetContent); willSetContent = null; } this.setState( { selectedCell: cell, sectionSelected: section, } ) } } /> ) } ) } ) ); } render() { const { attributes, setAttributes, className } = this.props; const { head, body, foot, maxWidth, tableCollapsed, hasFixedLayout } = attributes; const { initRow, initCol, selectedCell, rangeSelected, multiSelected } = this.state; const maxWidthVal = !!maxWidth ? maxWidth : undefined; const currentCell = selectedCell ? body[selectedCell.rowIndex].cells[selectedCell.colIndex] : null; // First time insert block, let user determine the table if (!body.length) { return (
this.setState( { initCol: value } ) } min="1" /> this.setState( { initRow: value } ) } min="1" />
{ __( 'Hint: Hold CTRL key for multi cells selection. Hold SHIFT key for range cells selection.' ) }
) } const TABLE_CONTROLS = [ { icon: 'table-row-before', title: __( 'Add Row Before' ), isDisabled: ! selectedCell || this.isRangeSelected() || this.isMultiSelected(), onClick: () => this.insertRow( 0 ), }, { icon: 'table-row-after', title: __( 'Add Row After' ), isDisabled: ! selectedCell || this.isRangeSelected() || this.isMultiSelected(), onClick: () => this.insertRow( 1 ), }, { icon: 'table-row-delete', title: __( 'Delete Row' ), isDisabled: ! selectedCell || this.isRangeSelected() || this.isMultiSelected(), onClick: () => this.deleteRow(), }, { icon: 'table-col-before', title: __( 'Add Column Before' ), isDisabled: ! selectedCell || this.isRangeSelected() || this.isMultiSelected(), onClick: () => this.insertColumn( 0 ), }, { icon: 'table-col-after', title: __( 'Add Column After' ), isDisabled: ! selectedCell || this.isRangeSelected() || this.isMultiSelected(), onClick: () => this.insertColumn( 1 ), }, { icon: 'table-col-delete', title: __( 'Delete Column' ), isDisabled: ! selectedCell || this.isRangeSelected() || this.isMultiSelected(), onClick: () => this.deleteColumn(), }, { icon: ( ), title: __( 'Split Merged Cells' ), isDisabled: ! selectedCell || (currentCell && !currentCell.rowSpan && !currentCell.colSpan) || this.isRangeSelected() || this.isMultiSelected(), onClick: () => this.splitMergedCells(), }, { icon: ( ), title: __( 'Merge Cells' ), isDisabled: !this.isRangeSelected(), onClick: () => this.mergeCells(), }, ]; let BORDER_SELECT = [ { title: __( 'Border Top' ), icon: ( ), onClick: () => this.updateCellsStyles( { setBorder: 'top' } ), }, { title: __( 'Border Right' ), icon: ( ), onClick: () => this.updateCellsStyles( { setBorder: 'right' } ), }, { title: __( 'Border Bottom' ), icon: ( ), onClick: () => this.updateCellsStyles( { setBorder: 'bottom' } ), }, { title: __( 'Border Left' ), icon: ( ), onClick: () => this.updateCellsStyles( { setBorder: 'left' } ), }, { title: __( 'Border All' ), icon: ( ), onClick: () => this.updateCellsStyles( { setBorder: 'all' } ), }, { title: __( 'Border None' ), icon: ( ), onClick: () => this.updateCellsStyles( { setBorder: 'none' } ), }, ]; if (this.isRangeSelected()) { const EXTRA_BORDER_SELECT = [ { title: __( 'Border Vertical' ), icon: ( ), onClick: () => this.updateCellsStyles( { setBorder: 'vert' } ), }, { title: __( 'Border Horizontal' ), icon: ( ), onClick: () => this.updateCellsStyles( { setBorder: 'horz' } ), }, { title: __( 'Border Inner' ), icon: ( ), onClick: () => this.updateCellsStyles( { setBorder: 'inner' } ), }, { title: __( 'Border Outer' ), icon: ( ), onClick: () => this.updateCellsStyles( { setBorder: 'outer' } ), }, ]; BORDER_SELECT = [...BORDER_SELECT, ...EXTRA_BORDER_SELECT]; } const HORZ_ALIGNMENT_CONTROLS = [ { icon: 'editor-alignleft', title: __( 'Align left' ), align: 'left', }, { icon: 'editor-aligncenter', title: __( 'Align center' ), align: 'center', }, { icon: 'editor-alignright', title: __( 'Align right' ), align: 'right', }, { icon: 'editor-justify', title: __( 'Align justify' ), align: 'justify', }, ]; const VERT_ALIGNMENT_CONTROLS = [ { icon: ( ), title: __( 'Align top' ), align: 'top', }, { icon: ( ), title: __( 'Align middle' ), align: 'middle', }, { icon: ( ), title: __( 'Align bottom' ), align: 'bottom', }, ]; return ( this.calculateRealColIndex() } /> setAttributes( { maxWidth: value } ) } /> setAttributes( { hasFixedLayout: !hasFixedLayout } ) } /> this.toggleSection( 'head' ) } /> this.toggleSection( 'foot' ) } /> setAttributes( { tableCollapsed: !tableCollapsed } ) } /> this.updateCellsStyles( { backgroundColor: value } ), }, { label: __( 'Text Color' ), value: this.getCellStyles( 'color' ), onChange: ( value ) => this.updateCellsStyles( { color: value } ), }, ] } />
{BORDER_SELECT.map( ( item, index ) => (
{ item.icon }
) ) }
this.updateCellsStyles( { borderStyle: value } ) } /> this.updateCellsStyles( { borderWidth: value } ) } /> this.updateCellsStyles( { borderColor: value } ), }, ] } />
this.updateCellsStyles( { paddingTop: value } ) } /> this.updateCellsStyles( { paddingRight: value } ) } /> this.updateCellsStyles( { paddingBottom: value } ) } /> this.updateCellsStyles( { paddingLeft: value } ) } /> { const isActive = ( this.getCellStyles( 'textAlign' ) === control.align ); return { ...control, isActive, onClick: () => this.updateCellsStyles( { textAlign: isActive ? undefined : control.align } ), }; } ) } /> { const isActive = ( this.getCellStyles( 'verticalAlign' ) === control.align ); return { ...control, isActive, onClick: () => this.updateCellsStyles( { verticalAlign: isActive ? undefined : control.align } ), }; } ) } />
{!!head.length && ( { this.renderSection( 'head' ) } ) } { this.renderSection( 'body' ) } {!!foot.length && ( { this.renderSection( 'foot' ) } ) }
) } } registerBlockType( 'advgb/table', { title: __( 'Advanced Table' ), description: __( 'Advanced table block with more styles and functions.' ), icon: { src: tableBlockIcon, foreground: typeof advgbBlocks !== 'undefined' ? advgbBlocks.color : undefined, }, category: 'advgb-category', keywords: [ __( 'table' ), __( 'cell' ), __( 'data' ) ], attributes: { head: { type: 'array', default: [], source: 'query', selector: 'thead tr', query: { cells: { type: 'array', default: [], source: 'query', selector: 'td, th', query: { content: { source: 'html', }, styles: { type: 'string', source: 'attribute', attribute: 'style', }, colSpan: { type: 'string', source: 'attribute', attribute: 'colspan', }, borderColorSaved: { type: 'string', source: 'attribute', attribute: 'data-border-color', } }, }, }, }, body: { type: 'array', default: [], source: 'query', selector: 'tbody tr', query: { cells: { type: 'array', default: [], source: 'query', selector: 'td', query: { content: { source: 'html', }, styles: { type: 'string', source: 'attribute', attribute: 'style', }, colSpan: { type: 'string', source: 'attribute', attribute: 'colspan', }, rowSpan: { type: 'string', source: 'attribute', attribute: 'rowspan', }, borderColorSaved: { type: 'string', source: 'attribute', attribute: 'data-border-color', } }, }, }, }, foot: { type: 'array', default: [], source: 'query', selector: 'tfoot tr', query: { cells: { type: 'array', default: [], source: 'query', selector: 'td, th', query: { content: { source: 'html', }, styles: { type: 'string', source: 'attribute', attribute: 'style', }, colSpan: { type: 'string', source: 'attribute', attribute: 'colspan', }, borderColorSaved: { type: 'string', source: 'attribute', attribute: 'data-border-color', } }, }, }, }, maxWidth: { type: 'number', default: 0 }, hasFixedLayout: { type: 'boolean', default: false, }, tableCollapsed: { type: 'boolean', default: false, }, changed: { type: 'boolean', default: false, }, }, supports: { align: true, }, styles: [ { name: 'default', label: __( 'Default' ), isDefault: true }, { name: 'stripes', label: __( 'Stripes' ) }, ], edit: AdvTable, save: function ( { attributes } ) { const { head, body, foot, maxWidth, tableCollapsed, hasFixedLayout } = attributes; const maxWidthVal = !!maxWidth ? maxWidth : undefined; function renderSection( section ) { return attributes[ section ].map( ( { cells }, rowIndex ) => ( { cells.map( ( { content, styles, colSpan, rowSpan, borderColorSaved }, colIndex ) => ( ) ) } ) ) } return ( {!!head.length && ( { renderSection( 'head' ) } ) } { renderSection( 'body' ) } {!!foot.length && ( { renderSection( 'foot' ) } ) }
); }, transforms: { from: [ { type: 'block', blocks: [ 'core/table' ], transform: ( attributes ) => { return createBlock( 'advgb/table', { body: attributes.body, } ) } }, ], }, } ); })( wp.i18n, wp.blocks, wp.element, wp.editor, wp.components );