UNPKG

@wordpress/block-library

Version:
344 lines (301 loc) 9.23 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createTable = createTable; exports.deleteColumn = deleteColumn; exports.deleteRow = deleteRow; exports.getCellAttribute = getCellAttribute; exports.getFirstRow = getFirstRow; exports.insertColumn = insertColumn; exports.insertRow = insertRow; exports.isCellSelected = isCellSelected; exports.isEmptyRow = isEmptyRow; exports.isEmptyTableSection = isEmptyTableSection; exports.toggleSection = toggleSection; exports.updateSelectedCell = updateSelectedCell; var _lodash = require("lodash"); /** * External dependencies */ const INHERITED_COLUMN_ATTRIBUTES = ['align']; /** * Creates a table state. * * @param {Object} options * @param {number} options.rowCount Row count for the table to create. * @param {number} options.columnCount Column count for the table to create. * * @return {Object} New table state. */ function createTable(_ref) { let { rowCount, columnCount } = _ref; return { body: Array.from({ length: rowCount }).map(() => ({ cells: Array.from({ length: columnCount }).map(() => ({ content: '', tag: 'td' })) })) }; } /** * Returns the first row in the table. * * @param {Object} state Current table state. * * @return {Object} The first table row. */ function getFirstRow(state) { if (!isEmptyTableSection(state.head)) { return state.head[0]; } if (!isEmptyTableSection(state.body)) { return state.body[0]; } if (!isEmptyTableSection(state.foot)) { return state.foot[0]; } } /** * Gets an attribute for a cell. * * @param {Object} state Current table state. * @param {Object} cellLocation The location of the cell * @param {string} attributeName The name of the attribute to get the value of. * * @return {*} The attribute value. */ function getCellAttribute(state, cellLocation, attributeName) { const { sectionName, rowIndex, columnIndex } = cellLocation; return (0, _lodash.get)(state, [sectionName, rowIndex, 'cells', columnIndex, attributeName]); } /** * Returns updated cell attributes after applying the `updateCell` function to the selection. * * @param {Object} state The block attributes. * @param {Object} selection The selection of cells to update. * @param {Function} updateCell A function to update the selected cell attributes. * * @return {Object} New table state including the updated cells. */ function updateSelectedCell(state, selection, updateCell) { if (!selection) { return state; } const tableSections = (0, _lodash.pick)(state, ['head', 'body', 'foot']); const { sectionName: selectionSectionName, rowIndex: selectionRowIndex } = selection; return (0, _lodash.mapValues)(tableSections, (section, sectionName) => { if (selectionSectionName && selectionSectionName !== sectionName) { return section; } return section.map((row, rowIndex) => { if (selectionRowIndex && selectionRowIndex !== rowIndex) { return row; } return { cells: row.cells.map((cellAttributes, columnIndex) => { const cellLocation = { sectionName, columnIndex, rowIndex }; if (!isCellSelected(cellLocation, selection)) { return cellAttributes; } return updateCell(cellAttributes); }) }; }); }); } /** * Returns whether the cell at `cellLocation` is included in the selection `selection`. * * @param {Object} cellLocation An object containing cell location properties. * @param {Object} selection An object containing selection properties. * * @return {boolean} True if the cell is selected, false otherwise. */ function isCellSelected(cellLocation, selection) { if (!cellLocation || !selection) { return false; } switch (selection.type) { case 'column': return selection.type === 'column' && cellLocation.columnIndex === selection.columnIndex; case 'cell': return selection.type === 'cell' && cellLocation.sectionName === selection.sectionName && cellLocation.columnIndex === selection.columnIndex && cellLocation.rowIndex === selection.rowIndex; } } /** * Inserts a row in the table state. * * @param {Object} state Current table state. * @param {Object} options * @param {string} options.sectionName Section in which to insert the row. * @param {number} options.rowIndex Row index at which to insert the row. * @param {number} options.columnCount Column count for the table to create. * * @return {Object} New table state. */ function insertRow(state, _ref2) { let { sectionName, rowIndex, columnCount } = _ref2; const firstRow = getFirstRow(state); const cellCount = columnCount === undefined ? (0, _lodash.get)(firstRow, ['cells', 'length']) : columnCount; // Bail early if the function cannot determine how many cells to add. if (!cellCount) { return state; } return { [sectionName]: [...state[sectionName].slice(0, rowIndex), { cells: Array.from({ length: cellCount }).map((_, index) => { const firstCellInColumn = (0, _lodash.get)(firstRow, ['cells', index], {}); const inheritedAttributes = (0, _lodash.pick)(firstCellInColumn, INHERITED_COLUMN_ATTRIBUTES); return { ...inheritedAttributes, content: '', tag: sectionName === 'head' ? 'th' : 'td' }; }) }, ...state[sectionName].slice(rowIndex)] }; } /** * Deletes a row from the table state. * * @param {Object} state Current table state. * @param {Object} options * @param {string} options.sectionName Section in which to delete the row. * @param {number} options.rowIndex Row index to delete. * * @return {Object} New table state. */ function deleteRow(state, _ref3) { let { sectionName, rowIndex } = _ref3; return { [sectionName]: state[sectionName].filter((row, index) => index !== rowIndex) }; } /** * Inserts a column in the table state. * * @param {Object} state Current table state. * @param {Object} options * @param {number} options.columnIndex Column index at which to insert the column. * * @return {Object} New table state. */ function insertColumn(state, _ref4) { let { columnIndex } = _ref4; const tableSections = (0, _lodash.pick)(state, ['head', 'body', 'foot']); return (0, _lodash.mapValues)(tableSections, (section, sectionName) => { // Bail early if the table section is empty. if (isEmptyTableSection(section)) { return section; } return section.map(row => { // Bail early if the row is empty or it's an attempt to insert past // the last possible index of the array. if (isEmptyRow(row) || row.cells.length < columnIndex) { return row; } return { cells: [...row.cells.slice(0, columnIndex), { content: '', tag: sectionName === 'head' ? 'th' : 'td' }, ...row.cells.slice(columnIndex)] }; }); }); } /** * Deletes a column from the table state. * * @param {Object} state Current table state. * @param {Object} options * @param {number} options.columnIndex Column index to delete. * * @return {Object} New table state. */ function deleteColumn(state, _ref5) { let { columnIndex } = _ref5; const tableSections = (0, _lodash.pick)(state, ['head', 'body', 'foot']); return (0, _lodash.mapValues)(tableSections, section => { // Bail early if the table section is empty. if (isEmptyTableSection(section)) { return section; } return section.map(row => ({ cells: row.cells.length >= columnIndex ? row.cells.filter((cell, index) => index !== columnIndex) : row.cells })).filter(row => row.cells.length); }); } /** * Toggles the existance of a section. * * @param {Object} state Current table state. * @param {string} sectionName Name of the section to toggle. * * @return {Object} New table state. */ function toggleSection(state, sectionName) { // Section exists, replace it with an empty row to remove it. if (!isEmptyTableSection(state[sectionName])) { return { [sectionName]: [] }; } // Get the length of the first row of the body to use when creating the header. const columnCount = (0, _lodash.get)(state, ['body', 0, 'cells', 'length'], 1); // Section doesn't exist, insert an empty row to create the section. return insertRow(state, { sectionName, rowIndex: 0, columnCount }); } /** * Determines whether a table section is empty. * * @param {Object} section Table section state. * * @return {boolean} True if the table section is empty, false otherwise. */ function isEmptyTableSection(section) { return !section || !section.length || (0, _lodash.every)(section, isEmptyRow); } /** * Determines whether a table row is empty. * * @param {Object} row Table row state. * * @return {boolean} True if the table section is empty, false otherwise. */ function isEmptyRow(row) { return !(row.cells && row.cells.length); } //# sourceMappingURL=state.js.map