diff --git a/packages/block-editor/src/autocompleters/block.js b/packages/block-editor/src/autocompleters/block.js
index 79cff960e3e71e..aa7dfa160ed738 100644
--- a/packages/block-editor/src/autocompleters/block.js
+++ b/packages/block-editor/src/autocompleters/block.js
@@ -1,13 +1,16 @@
/**
* External dependencies
*/
-import { noop, map, orderBy } from 'lodash';
+import { noop, orderBy } from 'lodash';
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
-import { createBlock } from '@wordpress/blocks';
+import {
+ createBlock,
+ createBlocksFromInnerBlocksTemplate,
+} from '@wordpress/blocks';
import { useMemo } from '@wordpress/element';
/**
@@ -19,18 +22,6 @@ import BlockIcon from '../components/block-icon';
const SHOWN_BLOCK_TYPES = 9;
-const createBlocksFromInnerBlocksTemplate = ( innerBlocksTemplate ) => {
- return map(
- innerBlocksTemplate,
- ( [ name, attributes, innerBlocks = [] ] ) =>
- createBlock(
- name,
- attributes,
- createBlocksFromInnerBlocksTemplate( innerBlocks )
- )
- );
-};
-
/** @typedef {import('@wordpress/block-editor').WPEditorInserterItem} WPEditorInserterItem */
/** @typedef {import('@wordpress/components').WPCompleter} WPCompleter */
diff --git a/packages/block-editor/src/components/block-variation-picker/index.native.js b/packages/block-editor/src/components/block-variation-picker/index.native.js
index 9ad927c0b4796a..eba775e09f278f 100644
--- a/packages/block-editor/src/components/block-variation-picker/index.native.js
+++ b/packages/block-editor/src/components/block-variation-picker/index.native.js
@@ -8,14 +8,13 @@ import {
TouchableWithoutFeedback,
Platform,
} from 'react-native';
-import { map } from 'lodash';
/**
* WordPress dependencies
*/
import { withSelect, useDispatch } from '@wordpress/data';
import { compose, usePreferredColorSchemeStyle } from '@wordpress/compose';
-import { createBlock } from '@wordpress/blocks';
+import { createBlocksFromInnerBlocksTemplate } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import {
PanelBody,
@@ -33,18 +32,6 @@ import styles from './style.scss';
const hitSlop = { top: 22, bottom: 22, left: 22, right: 22 };
-function createBlocksFromInnerBlocksTemplate( innerBlocksTemplate ) {
- return map(
- innerBlocksTemplate,
- ( [ name, attributes, innerBlocks = [] ] ) =>
- createBlock(
- name,
- attributes,
- createBlocksFromInnerBlocksTemplate( innerBlocks )
- )
- );
-}
-
function BlockVariationPicker( { isVisible, onClose, clientId, variations } ) {
const { replaceInnerBlocks } = useDispatch( 'core/block-editor' );
const isIOS = Platform.OS === 'ios';
diff --git a/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js b/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js
index 80cd70ba3013db..0ef5983c661d65 100644
--- a/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js
+++ b/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js
@@ -1,28 +1,13 @@
-/**
- * External dependencies
- */
-import { map } from 'lodash';
-
/**
* WordPress dependencies
*/
import { useEffect } from '@wordpress/element';
-import { createBlock } from '@wordpress/blocks';
+import {
+ createBlock,
+ createBlocksFromInnerBlocksTemplate,
+} from '@wordpress/blocks';
import { useSelect } from '@wordpress/data';
-// Copied over from the Columns block. It seems like it should become part of public API.
-const createBlocksFromInnerBlocksTemplate = ( innerBlocksTemplate ) => {
- return map(
- innerBlocksTemplate,
- ( [ name, attributes, innerBlocks = [] ] ) =>
- createBlock(
- name,
- attributes,
- createBlocksFromInnerBlocksTemplate( innerBlocks )
- )
- );
-};
-
/**
* Retrieves the block types inserter state.
*
diff --git a/packages/block-library/src/columns/edit.js b/packages/block-library/src/columns/edit.js
index 32a51f9ee202dd..39d537c0dedc81 100644
--- a/packages/block-library/src/columns/edit.js
+++ b/packages/block-library/src/columns/edit.js
@@ -2,7 +2,7 @@
* External dependencies
*/
import classnames from 'classnames';
-import { dropRight, get, map, times } from 'lodash';
+import { dropRight, get, times } from 'lodash';
/**
* WordPress dependencies
@@ -19,7 +19,10 @@ import {
useBlockProps,
} from '@wordpress/block-editor';
import { withDispatch, useDispatch, useSelect } from '@wordpress/data';
-import { createBlock } from '@wordpress/blocks';
+import {
+ createBlock,
+ createBlocksFromInnerBlocksTemplate,
+} from '@wordpress/blocks';
/**
* Internal dependencies
@@ -198,18 +201,6 @@ const ColumnsEditContainerWrapper = withDispatch(
} )
)( ColumnsEditContainer );
-const createBlocksFromInnerBlocksTemplate = ( innerBlocksTemplate ) => {
- return map(
- innerBlocksTemplate,
- ( [ name, attributes, innerBlocks = [] ] ) =>
- createBlock(
- name,
- attributes,
- createBlocksFromInnerBlocksTemplate( innerBlocks )
- )
- );
-};
-
function Placeholder( { clientId, name, setAttributes } ) {
const { blockType, defaultVariation, variations } = useSelect(
( select ) => {
diff --git a/packages/block-library/src/columns/index.js b/packages/block-library/src/columns/index.js
index 63676d74961b90..4de9408164f514 100644
--- a/packages/block-library/src/columns/index.js
+++ b/packages/block-library/src/columns/index.js
@@ -12,6 +12,7 @@ import edit from './edit';
import metadata from './block.json';
import save from './save';
import variations from './variations';
+import transforms from './transforms';
const { name } = metadata;
@@ -84,4 +85,5 @@ export const settings = {
deprecated,
edit,
save,
+ transforms,
};
diff --git a/packages/block-library/src/columns/transforms.js b/packages/block-library/src/columns/transforms.js
new file mode 100644
index 00000000000000..fc3e512a7d19f8
--- /dev/null
+++ b/packages/block-library/src/columns/transforms.js
@@ -0,0 +1,39 @@
+/**
+ * WordPress dependencies
+ */
+import {
+ createBlock,
+ createBlocksFromInnerBlocksTemplate,
+} from '@wordpress/blocks';
+
+const MAXIMUM_SELECTED_BLOCKS = 6;
+
+const transforms = {
+ from: [
+ {
+ type: 'block',
+ isMultiBlock: true,
+ blocks: [ '*' ],
+ __experimentalConvert: ( blocks ) => {
+ const columnWidth = +( 100 / blocks.length ).toFixed( 2 );
+ const innerBlocksTemplate = blocks.map(
+ ( { name, attributes, innerBlocks } ) => [
+ 'core/column',
+ { width: `${ columnWidth }%` },
+ [ [ name, { ...attributes }, innerBlocks ] ],
+ ]
+ );
+ return createBlock(
+ 'core/columns',
+ {},
+ createBlocksFromInnerBlocksTemplate( innerBlocksTemplate )
+ );
+ },
+ isMatch: ( { length: selectedBlocksLength } ) =>
+ selectedBlocksLength > 1 &&
+ selectedBlocksLength <= MAXIMUM_SELECTED_BLOCKS,
+ },
+ ],
+};
+
+export default transforms;
diff --git a/packages/blocks/README.md b/packages/blocks/README.md
index 1148b2f6028380..32a00fd9967d02 100644
--- a/packages/blocks/README.md
+++ b/packages/blocks/README.md
@@ -205,6 +205,21 @@ _Returns_
- `Object`: Block object.
+# **createBlocksFromInnerBlocksTemplate**
+
+Given an array of InnerBlocks templates or Block Objects,
+returns an array of created Blocks from them.
+It handles the case of having InnerBlocks as Blocks by
+converting them to the proper format to continue recursively.
+
+_Parameters_
+
+- _innerBlocksOrTemplate_ `Array`: Nested blocks or InnerBlocks templates.
+
+_Returns_
+
+- `Array