diff --git a/src/blocks/index.ts b/src/blocks/index.ts
index 69c7d83..8e2187f 100644
--- a/src/blocks/index.ts
+++ b/src/blocks/index.ts
@@ -7,6 +7,7 @@ import { registerBlockType } from '@wordpress/blocks';
* Import blocks.
*/
import * as table from './table';
+import * as tableRowContainer from './table-row-container';
import * as tableRow from './table-row';
import * as tableColumn from './table-column';
import * as tableCell from './table-cell';
@@ -21,6 +22,7 @@ import './block-toolbar';
*/
const blocks = [
table,
+ tableRowContainer,
tableRow,
tableColumn,
tableCell,
diff --git a/src/blocks/table-column/edit.tsx b/src/blocks/table-column/edit.tsx
index 1257e07..6df7efe 100644
--- a/src/blocks/table-column/edit.tsx
+++ b/src/blocks/table-column/edit.tsx
@@ -46,10 +46,15 @@ function TableColumnEdit( {
className: classnames( className, 'travelopia-table__column' ),
} );
- const tableId = context[ 'travelopia/table-id' ] as string;
+ const tableId: string = context[ 'travelopia/table-id' ] as string;
+ const rowContainerType: string = context[ 'travelopia/table-row-container-type' ] as string;
const innerBlocksProps = useInnerBlocksProps(
- { ...blockProps },
+ {
+ ...blockProps,
+ colSpan: attributes.colSpan,
+ rowSpan: attributes.rowSpan,
+ },
{
template: [ [ cellBlockName ] ],
templateLock: false,
@@ -78,6 +83,12 @@ function TableColumnEdit( {
setAttributes( { row, column } );
}, [ row, column, setAttributes ] );
+ // Determine tag.
+ let Tag: string = 'td';
+ if ( 'tbody' !== rowContainerType ) {
+ Tag = 'th';
+ }
+
return (
<>
-
|
>
);
diff --git a/src/blocks/table-column/index.tsx b/src/blocks/table-column/index.tsx
index e09465c..97e770b 100644
--- a/src/blocks/table-column/index.tsx
+++ b/src/blocks/table-column/index.tsx
@@ -46,6 +46,9 @@ export const settings: BlockConfiguration = {
'travelopia/table-row': 'row' as never,
'travelopia/table-column': 'column' as never,
},
+ usesContext: [
+ 'travelopia/table-row-container-type',
+ ],
supports: {
html: true,
color: {
@@ -55,6 +58,6 @@ export const settings: BlockConfiguration = {
},
edit,
save() {
- return | ;
+ return ;
},
};
diff --git a/src/blocks/table-row-container/edit.tsx b/src/blocks/table-row-container/edit.tsx
new file mode 100644
index 0000000..da97202
--- /dev/null
+++ b/src/blocks/table-row-container/edit.tsx
@@ -0,0 +1,70 @@
+/**
+ * WordPress dependencies.
+ */
+import { __ } from '@wordpress/i18n';
+import {
+ InspectorControls,
+ useBlockProps,
+ useInnerBlocksProps,
+} from '@wordpress/block-editor';
+import {
+ PanelBody,
+ ToggleControl,
+} from '@wordpress/components';
+import {
+ BlockEditProps,
+} from '@wordpress/blocks';
+
+/**
+ * External dependencies.
+ */
+import classnames from 'classnames';
+
+/**
+ * Internal dependencies.
+ */
+import { name as rowBlockName } from '../table-row';
+
+/**
+ * Edit function.
+ *
+ * @param {Object} props Edit properties.
+ *
+ * @return {JSX.Element} JSX Component.
+ */
+function TableRowContainerEdit( props: BlockEditProps ): JSX.Element {
+ // Block props.
+ const { className, attributes, setAttributes } = props;
+
+ // Inner block props.
+ const blockProps = useBlockProps( {
+ className: classnames( className, 'travelopia-table__row-container' ),
+ } );
+ const innerBlocksProps = useInnerBlocksProps( { ...blockProps }, {
+ allowedBlocks: [ rowBlockName ],
+ } );
+
+ // Determine tag.
+ const Tag: string = attributes.type;
+
+ // Return component.
+ return (
+ <>
+ { 'thead' === attributes.type &&
+
+
+ setAttributes( { isSticky } ) }
+ help={ __( 'Is this container sticky?', 'tp' ) }
+ />
+
+
+ }
+
+ >
+ );
+}
+
+export default TableRowContainerEdit;
diff --git a/src/blocks/table-row-container/index.tsx b/src/blocks/table-row-container/index.tsx
new file mode 100644
index 0000000..29d81e1
--- /dev/null
+++ b/src/blocks/table-row-container/index.tsx
@@ -0,0 +1,56 @@
+/**
+ * WordPress dependencies.
+ */
+import { __ } from '@wordpress/i18n';
+import { BlockConfiguration } from '@wordpress/blocks';
+import {
+ InnerBlocks,
+} from '@wordpress/block-editor';
+import {
+ blockTable as icon,
+} from '@wordpress/icons';
+
+/**
+ * Internal dependencies.
+ */
+import edit from './edit';
+
+/**
+ * Block data.
+ */
+export const name: string = 'travelopia/table-row-container';
+
+export const settings: BlockConfiguration = {
+ apiVersion: 3,
+ icon,
+ title: __( 'Row Container', 'tp' ),
+ description: __( 'A container for a row (THEAD, TBODY, TFOOT).', 'tp' ),
+ parent: [ 'travelopia/table' ],
+ category: 'text',
+ keywords: [
+ __( 'thead', 'tp' ),
+ __( 'tbody', 'tp' ),
+ __( 'tfoot', 'tp' ),
+ ],
+ attributes: {
+ type: {
+ type: 'string',
+ default: 'tbody',
+ },
+ isSticky: {
+ type: 'boolean',
+ default: false,
+ },
+ },
+ providesContext: {
+ 'travelopia/table-row-container-type': 'type' as never,
+ 'travelopia/table-row-container-sticky': 'isSticky' as never,
+ },
+ supports: {
+ html: false,
+ },
+ edit,
+ save() {
+ return ;
+ },
+};
diff --git a/src/blocks/table-row/edit.tsx b/src/blocks/table-row/edit.tsx
index 38a3843..5a69aed 100644
--- a/src/blocks/table-row/edit.tsx
+++ b/src/blocks/table-row/edit.tsx
@@ -1,11 +1,14 @@
/**
* WordPress dependencies.
*/
+import { __ } from '@wordpress/i18n';
import {
useBlockProps,
useInnerBlocksProps,
} from '@wordpress/block-editor';
-import { BlockEditProps } from '@wordpress/blocks';
+import {
+ BlockEditProps,
+} from '@wordpress/blocks';
/**
* External dependencies.
@@ -25,12 +28,16 @@ import { name as columnBlockName } from '../table-column';
* @return {JSX.Element} JSX Component.
*/
function TableRowEdit( props: BlockEditProps ): JSX.Element {
+ // Block props.
const { className } = props;
+
+ // Inner block props.
const blockProps = useBlockProps( {
className: classnames( className, 'travelopia-table__row' ),
} );
const innerBlocksProps = useInnerBlocksProps( { ...blockProps }, {
allowedBlocks: [ columnBlockName ],
+ templateLock: false,
} );
return (
diff --git a/src/blocks/table-row/index.tsx b/src/blocks/table-row/index.tsx
index af79522..6b9177d 100644
--- a/src/blocks/table-row/index.tsx
+++ b/src/blocks/table-row/index.tsx
@@ -25,10 +25,13 @@ export const settings: BlockConfiguration = {
icon,
title: __( 'Row', 'tp' ),
description: __( 'Individual row of the table.', 'tp' ),
- parent: [ 'travelopia/table' ],
+ parent: [ 'travelopia/table-row-container' ],
category: 'text',
keywords: [ __( 'row', 'tp' ) ],
attributes: {},
+ usesContext: [
+ 'travelopia/table-row-container-type',
+ ],
supports: {
html: true,
color: {
@@ -38,6 +41,6 @@ export const settings: BlockConfiguration = {
},
edit,
save() {
- return
;
+ return ;
},
};
diff --git a/src/blocks/table/edit.tsx b/src/blocks/table/edit.tsx
index 0554099..49b9824 100644
--- a/src/blocks/table/edit.tsx
+++ b/src/blocks/table/edit.tsx
@@ -1,12 +1,25 @@
/**
* WordPress dependencies.
*/
+import { __ } from '@wordpress/i18n';
import {
+ InspectorControls,
useBlockProps,
useInnerBlocksProps,
} from '@wordpress/block-editor';
-import { BlockEditProps } from '@wordpress/blocks';
+import {
+ BlockEditProps,
+ createBlock,
+} from '@wordpress/blocks';
import { useEffect } from '@wordpress/element';
+import {
+ PanelBody,
+ ToggleControl,
+} from '@wordpress/components';
+import {
+ select,
+ dispatch,
+} from '@wordpress/data';
/**
* External dependencies.
@@ -17,7 +30,78 @@ import classnames from 'classnames';
* Internal dependencies.
*/
import { TablePlaceholder } from './placeholder';
+import { name as rowContainerBlockName } from '../table-row-container';
import { name as rowBlockName } from '../table-row';
+import { name as columnBlockName } from '../table-column';
+import { name as cellBlockName } from '../table-cell';
+
+/**
+ * Create and insert a row container.
+ *
+ * @param {string} type Row container type.
+ * @param {string} tableClientId The table block's client ID.
+ */
+export const createAndInsertRowContainer = ( type: string = 'tbody', tableClientId: string = '' ): void => {
+ // Get table block.
+ const tableBlock = select( 'core/block-editor' ).getBlock( tableClientId );
+ if ( ! tableBlock ) {
+ return;
+ }
+
+ // Create row container.
+ const rowContainerBlock = createBlock( rowContainerBlockName, { type } );
+
+ // Determine number of rows to create.
+ let totalRows = tableBlock.attributes.rows;
+ if ( 'tbody' !== type ) {
+ totalRows = 1;
+ }
+
+ // Add rows and columns to it.
+ for ( let i: number = 0; i < totalRows; i++ ) {
+ const columnBlocks = [];
+ for ( let j: number = 0; j < tableBlock.attributes.columns; j++ ) {
+ columnBlocks.push(
+ createBlock( columnBlockName, {}, [
+ createBlock( cellBlockName ),
+ ] )
+ );
+ }
+
+ rowContainerBlock.innerBlocks.push(
+ createBlock( rowBlockName, {}, columnBlocks )
+ );
+ }
+
+ // Add newly created row and column blocks to the table.
+ if ( 'tbody' === type ) {
+ dispatch( 'core/block-editor' ).replaceInnerBlocks( tableClientId, [ rowContainerBlock ] );
+ } else {
+ const position = 'thead' === type ? 0 : tableBlock.innerBlocks.length;
+ dispatch( 'core/block-editor' ).insertBlock( rowContainerBlock, position, tableClientId );
+ }
+};
+
+/**
+ * Delete row container child block.
+ *
+ * @param {string} type Row container type.
+ * @param {string} tableClientId The table block's client ID.
+ */
+export const deleteRowContainer = ( type: string = 'thead', tableClientId: string = '' ): void => {
+ // Get table block.
+ const tableBlock = select( 'core/block-editor' ).getBlock( tableClientId );
+ if ( ! tableBlock || ! tableBlock.innerBlocks.length ) {
+ return;
+ }
+
+ // Find the child block and delete it.
+ tableBlock.innerBlocks.forEach( ( innerBlock ) => {
+ if ( innerBlock.attributes?.type === type ) {
+ dispatch( 'core/block-editor' ).removeBlock( innerBlock.clientId );
+ }
+ } );
+};
/**
* Edit function.
@@ -32,7 +116,8 @@ function TableEdit( props: BlockEditProps ): JSX.Element {
className: classnames( className, 'travelopia-table' ),
} );
const innerBlocksProps = useInnerBlocksProps( {}, {
- allowedBlocks: [ rowBlockName ],
+ allowedBlocks: [ rowContainerBlockName ],
+ renderAppender: undefined,
} );
// Set blockId attribute.
@@ -40,18 +125,64 @@ function TableEdit( props: BlockEditProps ): JSX.Element {
setAttributes( { blockId: clientId } );
}, [ clientId, setAttributes ] );
+ /**
+ * Handle THEAD change.
+ *
+ * @param {boolean} hasThead Has THEAD.
+ */
+ const handleTheadChange = ( hasThead: boolean ): void => {
+ if ( hasThead ) {
+ createAndInsertRowContainer( 'thead', clientId );
+ } else {
+ deleteRowContainer( 'thead', clientId );
+ }
+ setAttributes( { hasThead } );
+ };
+
+ /**
+ * Handle TFOOT change.
+ *
+ * @param {boolean} hasTfoot Has TFOOT.
+ */
+ const handleTfootChange = ( hasTfoot: boolean ): void => {
+ if ( hasTfoot ) {
+ createAndInsertRowContainer( 'tfoot', clientId );
+ } else {
+ deleteRowContainer( 'tfoot', clientId );
+ }
+ setAttributes( { hasTfoot } );
+ };
+
return (
-
- {
- /* Placeholder for initial state. */
- ( 0 === attributes.rows || 0 === attributes.columns ) &&
-
- }
- {
- ( 0 !== attributes.rows || 0 !== attributes.columns ) &&
-
- }
-
+ <>
+
+
+
+
+
+
+
+ {
+ /* Placeholder for initial state. */
+ ( 0 === attributes.rows || 0 === attributes.columns ) &&
+
+ }
+ {
+ ( 0 !== attributes.rows || 0 !== attributes.columns ) &&
+
+ }
+
+ >
);
}
diff --git a/src/blocks/table/index.tsx b/src/blocks/table/index.tsx
index 46abaef..88a55bf 100644
--- a/src/blocks/table/index.tsx
+++ b/src/blocks/table/index.tsx
@@ -47,15 +47,27 @@ export const settings: BlockConfiguration = {
blockId: {
type: 'string',
},
+ hasThead: {
+ type: 'boolean',
+ default: false,
+ },
+ hasTfoot: {
+ type: 'boolean',
+ default: false,
+ },
},
providesContext: {
'travelopia/table-id': 'blockId' as never,
+ 'travelopia/table-total-rows': 'rows' as never,
+ 'travelopia/table-total-columns': 'columns' as never,
+ 'travelopia/table-has-thead': 'hasThead' as never,
+ 'travelopia/table-has-tfoot': 'hasTfoot' as never,
},
supports: {
anchor: true,
},
edit,
save() {
- return ;
+ return ;
},
};
diff --git a/src/blocks/table/placeholder.tsx b/src/blocks/table/placeholder.tsx
index 0f55ada..4da53f8 100644
--- a/src/blocks/table/placeholder.tsx
+++ b/src/blocks/table/placeholder.tsx
@@ -12,19 +12,12 @@ import {
import { useState } from '@wordpress/element';
import {
BlockEditProps,
- createBlock,
} from '@wordpress/blocks';
-import {
- select,
- dispatch,
-} from '@wordpress/data';
/**
* Internal dependencies.
*/
-import { name as rowBlockName } from '../table-row';
-import { name as columnBlockName } from '../table-column';
-import { name as cellBlockName } from '../table-cell';
+import { createAndInsertRowContainer } from './edit';
/**
* Edit function.
@@ -53,32 +46,8 @@ export function TablePlaceholder( props: BlockEditProps ): JSX.Element {
// Set attributes.
setAttributes( { rows, columns } );
- // Get current block.
- const currentBlock = select( 'core/block-editor' ).getBlock( clientId );
- if ( ! currentBlock ) {
- return;
- }
-
- // Create row and column blocks.
- const innerBlocks = [];
-
- for ( let i: number = 0; i < rows; i++ ) {
- const columnBlocks = [];
- for ( let j: number = 0; j < columns; j++ ) {
- columnBlocks.push(
- createBlock( columnBlockName, {}, [
- createBlock( cellBlockName ),
- ] )
- );
- }
-
- innerBlocks.push(
- createBlock( rowBlockName, {}, columnBlocks )
- );
- }
-
- // Add newly created row and column blocks to the table.
- dispatch( 'core/block-editor' ).replaceInnerBlocks( clientId, innerBlocks );
+ // Create and insert row container.
+ createAndInsertRowContainer( 'tbody', clientId );
} }
>