@wordpress/block-library
Version:
Block library for the WordPress editor.
344 lines (301 loc) • 9.23 kB
JavaScript
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
;