2023-08-02 07:09:51 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
2023-09-05 12:52:19 +00:00
|
|
|
import { BlockAttributes, BlockInstance } from '@wordpress/blocks';
|
2023-08-02 07:09:51 +00:00
|
|
|
import { select, dispatch } from '@wordpress/data';
|
|
|
|
|
2023-09-08 14:06:48 +00:00
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import { ThumbnailsPosition } from './inner-blocks/product-gallery-thumbnails/constants';
|
2023-09-21 11:31:16 +00:00
|
|
|
import { getNextPreviousImagesWithClassName } from './inner-blocks/product-gallery-large-image-next-previous/utils';
|
|
|
|
import { NextPreviousButtonSettingValues } from './inner-blocks/product-gallery-large-image-next-previous/types';
|
2023-09-08 14:06:48 +00:00
|
|
|
|
2023-08-02 07:09:51 +00:00
|
|
|
/**
|
|
|
|
* Generates layout attributes based on the position of thumbnails.
|
|
|
|
*
|
|
|
|
* @param {string} thumbnailsPosition - The position of thumbnails ('bottom' or other values).
|
|
|
|
* @return {{type: string, orientation?: string, flexWrap?: string}} - An object representing layout attributes.
|
|
|
|
*/
|
|
|
|
export const getGroupLayoutAttributes = (
|
|
|
|
thumbnailsPosition: string
|
|
|
|
): { type: string; orientation?: string; flexWrap?: string } => {
|
|
|
|
switch ( thumbnailsPosition ) {
|
|
|
|
case 'bottom':
|
|
|
|
// Stack
|
|
|
|
return { type: 'flex', orientation: 'vertical' };
|
2023-09-21 11:31:16 +00:00
|
|
|
case 'off':
|
|
|
|
// Stack
|
|
|
|
return { type: 'flex', orientation: 'vertical' };
|
2023-08-02 07:09:51 +00:00
|
|
|
default:
|
|
|
|
// Row
|
|
|
|
return { type: 'flex', flexWrap: 'nowrap' };
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns inner block lock attributes based on provided action.
|
|
|
|
*
|
|
|
|
* @param {string} action - The action to take on the inner blocks ('lock' or 'unlock').
|
|
|
|
* @return {{lock: {move?: boolean, remove?: boolean}}} - An object representing lock attributes for inner blocks.
|
|
|
|
*/
|
|
|
|
export const getInnerBlocksLockAttributes = (
|
|
|
|
action: string
|
|
|
|
): { lock: { move?: boolean; remove?: boolean } } => {
|
|
|
|
switch ( action ) {
|
|
|
|
case 'lock':
|
|
|
|
return { lock: { move: true, remove: true } };
|
|
|
|
case 'unlock':
|
|
|
|
return { lock: {} };
|
|
|
|
default:
|
|
|
|
return { lock: {} };
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates block attributes based on provided attributes.
|
|
|
|
*
|
|
|
|
* @param {BlockAttributes} attributesToUpdate - The new attributes to set on the block.
|
|
|
|
* @param {BlockAttributes | undefined} block - The block object to update.
|
|
|
|
*/
|
|
|
|
export const updateBlockAttributes = (
|
|
|
|
attributesToUpdate: BlockAttributes,
|
|
|
|
block: BlockAttributes | undefined
|
|
|
|
): void => {
|
|
|
|
if ( block !== undefined ) {
|
|
|
|
const updatedBlock = {
|
|
|
|
...block,
|
|
|
|
attributes: {
|
|
|
|
...block.attributes,
|
|
|
|
...attributesToUpdate,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
dispatch( 'core/block-editor' ).updateBlock(
|
|
|
|
block.clientId,
|
|
|
|
updatedBlock
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-09-05 12:52:19 +00:00
|
|
|
const controlBlocksLockAttribute = ( {
|
|
|
|
blocks,
|
|
|
|
lockBlocks,
|
|
|
|
}: {
|
|
|
|
blocks: BlockInstance[];
|
|
|
|
lockBlocks: boolean;
|
|
|
|
} ) => {
|
|
|
|
for ( const block of blocks ) {
|
|
|
|
if ( lockBlocks ) {
|
|
|
|
updateBlockAttributes(
|
|
|
|
getInnerBlocksLockAttributes( 'lock' ),
|
|
|
|
block
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
updateBlockAttributes(
|
|
|
|
getInnerBlocksLockAttributes( 'unlock' ),
|
|
|
|
block
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Recursively searches through an array of `BlockInstance` objects and their nested `innerBlocks` arrays to find a block that matches a given condition.
|
|
|
|
*
|
|
|
|
* @param { { blocks: BlockInstance[], findCondition: Function } } parameters Parameters containing an array of `BlockInstance` objects to search through and a function that takes a `BlockInstance` object as its argument and returns a boolean indicating whether the block matches the desired condition.
|
|
|
|
* @return If a matching block is found, the function returns the `BlockInstance` object. If no matching block is found, the function returns `undefined`.
|
|
|
|
*/
|
|
|
|
const findBlock = ( {
|
|
|
|
blocks,
|
|
|
|
findCondition,
|
|
|
|
}: {
|
|
|
|
blocks: BlockInstance[];
|
|
|
|
findCondition: ( block: BlockInstance ) => boolean;
|
|
|
|
} ): BlockInstance | undefined => {
|
|
|
|
for ( const block of blocks ) {
|
|
|
|
if ( findCondition( block ) ) {
|
|
|
|
return block;
|
|
|
|
}
|
|
|
|
if ( block.innerBlocks ) {
|
|
|
|
const largeImageParentBlock = findBlock( {
|
|
|
|
blocks: block.innerBlocks,
|
|
|
|
findCondition,
|
|
|
|
} );
|
|
|
|
if ( largeImageParentBlock ) {
|
|
|
|
return largeImageParentBlock;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
};
|
|
|
|
|
2023-09-08 14:06:48 +00:00
|
|
|
/**
|
|
|
|
* Sets the layout of group block based on the thumbnails' position.
|
|
|
|
*
|
|
|
|
* @param {ThumbnailsPosition} thumbnailsPosition - The position of thumbnails.
|
|
|
|
* @param {string} clientId - The client ID of the block to update.
|
|
|
|
*/
|
|
|
|
const setGroupBlockLayoutByThumbnailsPosition = (
|
|
|
|
thumbnailsPosition: ThumbnailsPosition,
|
|
|
|
clientId: string
|
|
|
|
): void => {
|
|
|
|
const block = select( 'core/block-editor' ).getBlock( clientId );
|
|
|
|
block?.innerBlocks.forEach( ( innerBlock ) => {
|
|
|
|
if ( innerBlock.name === 'core/group' ) {
|
|
|
|
updateBlockAttributes(
|
|
|
|
{
|
|
|
|
layout: getGroupLayoutAttributes( thumbnailsPosition ),
|
|
|
|
},
|
|
|
|
innerBlock
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
2023-08-02 07:09:51 +00:00
|
|
|
/**
|
|
|
|
* Moves inner blocks to a position based on provided attributes.
|
|
|
|
*
|
|
|
|
* @param {BlockAttributes} attributes - The attributes of the parent block.
|
|
|
|
* @param {string} clientId - The clientId of the parent block.
|
|
|
|
*/
|
|
|
|
export const moveInnerBlocksToPosition = (
|
|
|
|
attributes: BlockAttributes,
|
|
|
|
clientId: string
|
|
|
|
): void => {
|
2023-09-05 12:52:19 +00:00
|
|
|
const { getBlock, getBlockRootClientId, getBlockIndex } =
|
|
|
|
select( 'core/block-editor' );
|
|
|
|
const { moveBlockToPosition } = dispatch( 'core/block-editor' );
|
|
|
|
const productGalleryBlock = getBlock( clientId );
|
2023-08-02 07:09:51 +00:00
|
|
|
|
2023-09-05 12:52:19 +00:00
|
|
|
if ( productGalleryBlock ) {
|
|
|
|
const thumbnailsBlock = findBlock( {
|
|
|
|
blocks: [ productGalleryBlock ],
|
|
|
|
findCondition( block ) {
|
|
|
|
return block.name === 'woocommerce/product-gallery-thumbnails';
|
|
|
|
},
|
|
|
|
} );
|
|
|
|
const largeImageParentBlock = findBlock( {
|
|
|
|
blocks: [ productGalleryBlock ],
|
|
|
|
findCondition( block ) {
|
|
|
|
return Boolean(
|
|
|
|
block.innerBlocks?.find(
|
|
|
|
( innerBlock ) =>
|
|
|
|
innerBlock.name ===
|
|
|
|
'woocommerce/product-gallery-large-image'
|
|
|
|
)
|
2023-08-02 07:09:51 +00:00
|
|
|
);
|
2023-09-05 12:52:19 +00:00
|
|
|
},
|
|
|
|
} );
|
|
|
|
const largeImageParentBlockIndex = getBlockIndex(
|
|
|
|
largeImageParentBlock?.clientId || ''
|
|
|
|
);
|
|
|
|
const thumbnailsBlockIndex = getBlockIndex(
|
|
|
|
thumbnailsBlock?.clientId || ''
|
|
|
|
);
|
2023-08-02 07:09:51 +00:00
|
|
|
|
2023-09-05 12:52:19 +00:00
|
|
|
if (
|
|
|
|
largeImageParentBlock &&
|
|
|
|
thumbnailsBlock &&
|
|
|
|
largeImageParentBlockIndex !== -1 &&
|
|
|
|
thumbnailsBlockIndex !== -1
|
|
|
|
) {
|
|
|
|
controlBlocksLockAttribute( {
|
|
|
|
blocks: [ thumbnailsBlock, largeImageParentBlock ],
|
|
|
|
lockBlocks: false,
|
|
|
|
} );
|
2023-08-02 07:09:51 +00:00
|
|
|
|
2023-09-05 12:52:19 +00:00
|
|
|
const { thumbnailsPosition } = attributes;
|
2023-09-08 14:06:48 +00:00
|
|
|
setGroupBlockLayoutByThumbnailsPosition(
|
|
|
|
thumbnailsPosition,
|
|
|
|
clientId
|
|
|
|
);
|
2023-08-02 07:09:51 +00:00
|
|
|
|
2023-09-05 12:52:19 +00:00
|
|
|
if (
|
|
|
|
( ( thumbnailsPosition === 'bottom' ||
|
|
|
|
thumbnailsPosition === 'right' ) &&
|
|
|
|
thumbnailsBlockIndex < largeImageParentBlockIndex ) ||
|
|
|
|
( thumbnailsPosition === 'left' &&
|
|
|
|
thumbnailsBlockIndex > largeImageParentBlockIndex )
|
|
|
|
) {
|
|
|
|
moveBlockToPosition(
|
|
|
|
thumbnailsBlock.clientId,
|
|
|
|
getBlockRootClientId( thumbnailsBlock.clientId ) ||
|
|
|
|
undefined,
|
|
|
|
getBlockRootClientId( largeImageParentBlock.clientId ) ||
|
|
|
|
undefined,
|
|
|
|
largeImageParentBlockIndex
|
2023-08-02 07:09:51 +00:00
|
|
|
);
|
|
|
|
}
|
2023-09-05 12:52:19 +00:00
|
|
|
|
|
|
|
controlBlocksLockAttribute( {
|
|
|
|
blocks: [ thumbnailsBlock, largeImageParentBlock ],
|
|
|
|
lockBlocks: true,
|
|
|
|
} );
|
2023-08-02 07:09:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2023-09-21 11:31:16 +00:00
|
|
|
|
|
|
|
export const getClassNameByNextPreviousButtonsPosition = (
|
|
|
|
nextPreviousButtonsPosition: NextPreviousButtonSettingValues
|
|
|
|
) => {
|
|
|
|
return `wc-block-product-gallery--has-next-previous-buttons-${
|
|
|
|
getNextPreviousImagesWithClassName( nextPreviousButtonsPosition )
|
|
|
|
?.classname
|
|
|
|
}`;
|
|
|
|
};
|