utils.js
335 lines
| 1 | import hasNumericValue from '../../utils/has-numeric-value'; |
| 2 | import sizingValue from '../../utils/sizingValue'; |
| 3 | import { createBlock } from '@wordpress/blocks'; |
| 4 | |
| 5 | /** |
| 6 | * Returns migrated layout options for new Layout panel in 1.7.0. |
| 7 | * |
| 8 | * @param {Object} attributes The block attributes. |
| 9 | * @return {Object} The new attributes. |
| 10 | */ |
| 11 | function getLayoutAttributes( attributes ) { |
| 12 | const { |
| 13 | isGrid, |
| 14 | verticalAlignment, |
| 15 | verticalAlignmentTablet, |
| 16 | verticalAlignmentMobile, |
| 17 | sizing, |
| 18 | bgImage, |
| 19 | bgOptions, |
| 20 | useDynamicData, |
| 21 | dynamicContentType, |
| 22 | zindex, |
| 23 | gradient, |
| 24 | gradientSelector, |
| 25 | shapeDividers, |
| 26 | linkType, |
| 27 | url, |
| 28 | dynamicLinkType, |
| 29 | advBackgrounds, |
| 30 | gpInlinePostMeta, |
| 31 | gpInlinePostMetaJustify, |
| 32 | gpInlinePostMetaJustifyTablet, |
| 33 | gpInlinePostMetaJustifyMobile, |
| 34 | } = attributes; |
| 35 | |
| 36 | const layoutAttributes = {}; |
| 37 | let setMinHeightFlex = false; |
| 38 | |
| 39 | if ( sizingValue( 'minHeight', sizing ) && verticalAlignment && ! isGrid ) { |
| 40 | layoutAttributes.display = 'flex'; |
| 41 | layoutAttributes.flexDirection = 'column'; |
| 42 | layoutAttributes.justifyContent = verticalAlignment; |
| 43 | setMinHeightFlex = true; |
| 44 | } |
| 45 | |
| 46 | if ( isGrid && verticalAlignment ) { |
| 47 | layoutAttributes.display = 'flex'; |
| 48 | layoutAttributes.flexDirection = 'column'; |
| 49 | layoutAttributes.justifyContent = verticalAlignment; |
| 50 | } |
| 51 | |
| 52 | if ( ! isGrid ) { |
| 53 | if ( ! setMinHeightFlex && sizingValue( 'minHeightTablet', sizing ) && 'inherit' !== verticalAlignmentTablet ) { |
| 54 | layoutAttributes.displayTablet = 'flex'; |
| 55 | layoutAttributes.flexDirectionTablet = 'column'; |
| 56 | setMinHeightFlex = true; |
| 57 | } |
| 58 | |
| 59 | if ( setMinHeightFlex && 'inherit' !== verticalAlignmentTablet ) { |
| 60 | layoutAttributes.justifyContentTablet = verticalAlignmentTablet; |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | if ( isGrid && verticalAlignmentTablet && 'inherit' !== verticalAlignmentTablet ) { |
| 65 | layoutAttributes.justifyContentTablet = verticalAlignmentTablet; |
| 66 | } |
| 67 | |
| 68 | if ( ! isGrid ) { |
| 69 | if ( ! setMinHeightFlex && sizingValue( 'minHeightMobile', sizing ) && 'inherit' !== verticalAlignmentMobile ) { |
| 70 | layoutAttributes.displayMobile = 'flex'; |
| 71 | layoutAttributes.flexDirectionMobile = 'column'; |
| 72 | setMinHeightFlex = true; |
| 73 | } |
| 74 | |
| 75 | if ( setMinHeightFlex && 'inherit' !== verticalAlignmentTablet ) { |
| 76 | layoutAttributes.justifyContentMobile = verticalAlignmentMobile; |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | if ( isGrid && verticalAlignmentMobile && 'inherit' !== verticalAlignmentMobile ) { |
| 81 | layoutAttributes.justifyContentMobile = verticalAlignmentMobile; |
| 82 | } |
| 83 | |
| 84 | const hasBgImage = !! bgImage || ( useDynamicData && '' !== dynamicContentType ); |
| 85 | |
| 86 | if ( zindex || shapeDividers.length ) { |
| 87 | layoutAttributes.position = 'relative'; |
| 88 | } |
| 89 | |
| 90 | if ( |
| 91 | ( hasBgImage && 'pseudo-element' === bgOptions.selector ) || |
| 92 | ( gradient && 'pseudo-element' === gradientSelector ) || |
| 93 | advBackgrounds?.length |
| 94 | ) { |
| 95 | layoutAttributes.position = 'relative'; |
| 96 | layoutAttributes.overflowX = 'hidden'; |
| 97 | layoutAttributes.overflowY = 'hidden'; |
| 98 | } |
| 99 | |
| 100 | // GB Pro features. |
| 101 | if ( 'hidden-link' === linkType && ( url || ( useDynamicData && dynamicLinkType ) ) ) { |
| 102 | layoutAttributes.position = 'relative'; |
| 103 | } |
| 104 | |
| 105 | // GP Premium feature. |
| 106 | if ( gpInlinePostMeta ) { |
| 107 | layoutAttributes.display = 'flex'; |
| 108 | layoutAttributes.alignItems = 'center'; |
| 109 | layoutAttributes.gpInlinePostMeta = false; |
| 110 | |
| 111 | if ( gpInlinePostMetaJustify ) { |
| 112 | layoutAttributes.justifyContent = gpInlinePostMetaJustify; |
| 113 | layoutAttributes.gpInlinePostMetaJustify = ''; |
| 114 | } |
| 115 | |
| 116 | if ( gpInlinePostMetaJustifyTablet ) { |
| 117 | layoutAttributes.justifyContentTablet = gpInlinePostMetaJustifyTablet; |
| 118 | layoutAttributes.gpInlinePostMetaJustifyTablet = ''; |
| 119 | } |
| 120 | |
| 121 | if ( gpInlinePostMetaJustifyMobile ) { |
| 122 | layoutAttributes.justifyContentMobile = gpInlinePostMetaJustifyMobile; |
| 123 | layoutAttributes.gpInlinePostMetaJustifyMobile = ''; |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | return layoutAttributes; |
| 128 | } |
| 129 | |
| 130 | /** |
| 131 | * Check if we should wrap block contents in an additional Container. |
| 132 | * |
| 133 | * @param {Object} props The function props. |
| 134 | * @return {boolean} Whether to add an inner Container. |
| 135 | */ |
| 136 | function shouldMigrateInnerContainer( props ) { |
| 137 | const { |
| 138 | attributes, |
| 139 | insideGridBlock, |
| 140 | childBlock, |
| 141 | } = props; |
| 142 | |
| 143 | const { |
| 144 | outerContainer, |
| 145 | innerContainer, |
| 146 | innerZindex, |
| 147 | } = attributes; |
| 148 | |
| 149 | let recommend = false; |
| 150 | |
| 151 | if ( 'full' === outerContainer && 'contained' === innerContainer ) { |
| 152 | recommend = true; |
| 153 | } |
| 154 | |
| 155 | if ( insideGridBlock ) { |
| 156 | recommend = false; |
| 157 | } |
| 158 | |
| 159 | if ( childBlock && 'generateblocks/container' === childBlock.name ) { |
| 160 | // This block already has a contained inner Container. |
| 161 | if ( 'contained' === childBlock.attributes.outerContainer ) { |
| 162 | recommend = false; |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | if ( hasNumericValue( innerZindex ) ) { |
| 167 | recommend = true; |
| 168 | } |
| 169 | |
| 170 | // Some effects target the inner container. |
| 171 | const effects = [ 'opacities', 'transitions', 'boxShadows', 'transforms', 'textShadows', 'filters' ]; |
| 172 | |
| 173 | effects.forEach( ( effect ) => { |
| 174 | const hasInnerContainerEffect = |
| 175 | attributes[ effect ] && |
| 176 | attributes[ effect ].some( ( type ) => 'innerContainer' === type.target ); |
| 177 | |
| 178 | if ( hasInnerContainerEffect ) { |
| 179 | recommend = true; |
| 180 | } |
| 181 | } ); |
| 182 | |
| 183 | return recommend; |
| 184 | } |
| 185 | |
| 186 | /** |
| 187 | * Wrap all child blocks in an additional Container block. |
| 188 | * |
| 189 | * @param {Object} props The function props. |
| 190 | */ |
| 191 | function doInnerContainerMigration( props ) { |
| 192 | const { |
| 193 | attributes, |
| 194 | setAttributes, |
| 195 | parentBlock, |
| 196 | replaceBlocks, |
| 197 | insertBlocks, |
| 198 | clientId, |
| 199 | } = props; |
| 200 | |
| 201 | const { |
| 202 | isGrid, |
| 203 | containerWidth, |
| 204 | outerContainer, |
| 205 | innerContainer, |
| 206 | innerZindex, |
| 207 | useGlobalMaxWidth, |
| 208 | spacing, |
| 209 | } = attributes; |
| 210 | |
| 211 | const childBlocks = parentBlock.innerBlocks; |
| 212 | const hasDefaultContainerWidth = parseInt( containerWidth ) === parseInt( generateBlocksInfo.globalContainerWidth ); |
| 213 | const layoutAttributes = getLayoutAttributes( attributes ); |
| 214 | |
| 215 | // We need to create new block instances for each inner block |
| 216 | // to prevent a bug where re-usable block content is removed |
| 217 | // during migration. |
| 218 | const newChildBlocks = childBlocks.map( ( block ) => { |
| 219 | return createBlock( |
| 220 | block.name, |
| 221 | block.attributes, |
| 222 | block.innerBlocks |
| 223 | ); |
| 224 | } ); |
| 225 | |
| 226 | // Wrap our existing child blocks in a new Container block. |
| 227 | const newInnerBlocks = createBlock( |
| 228 | 'generateblocks/container', |
| 229 | { |
| 230 | spacing: { |
| 231 | paddingTop: spacing?.paddingTop, |
| 232 | paddingRight: spacing?.paddingRight, |
| 233 | paddingBottom: spacing?.paddingBottom, |
| 234 | paddingLeft: spacing?.paddingLeft, |
| 235 | paddingTopTablet: spacing?.paddingTopTablet, |
| 236 | paddingRightTablet: spacing?.paddingRightTablet, |
| 237 | paddingBottomTablet: spacing?.paddingBottomTablet, |
| 238 | paddingLeftTablet: spacing?.paddingLeftTablet, |
| 239 | paddingTopMobile: spacing?.paddingTopMobile, |
| 240 | paddingRightMobile: spacing?.paddingRightMobile, |
| 241 | paddingBottomMobile: spacing?.paddingBottomMobile, |
| 242 | paddingLeftMobile: spacing?.paddingLeftMobile, |
| 243 | marginLeft: 'auto', |
| 244 | marginRight: 'auto', |
| 245 | }, |
| 246 | useGlobalMaxWidth: 'contained' === innerContainer ? !! hasDefaultContainerWidth : useGlobalMaxWidth, |
| 247 | sizing: { |
| 248 | maxWidth: 'contained' === innerContainer && ! hasDefaultContainerWidth && containerWidth ? containerWidth + 'px' : '', |
| 249 | }, |
| 250 | zindex: innerZindex, |
| 251 | position: innerZindex || 0 === innerZindex ? 'relative' : '', |
| 252 | }, |
| 253 | newChildBlocks |
| 254 | ); |
| 255 | |
| 256 | const childClientIds = childBlocks.map( ( block ) => block.clientId ); |
| 257 | |
| 258 | if ( childClientIds.length > 0 ) { |
| 259 | replaceBlocks( childClientIds, newInnerBlocks ); |
| 260 | } else { |
| 261 | insertBlocks( newInnerBlocks, 0, clientId ); |
| 262 | } |
| 263 | |
| 264 | // Update attributes for existing Container block. |
| 265 | setAttributes( { |
| 266 | useInnerContainer: false, |
| 267 | spacing: { |
| 268 | paddingTop: '', |
| 269 | paddingRight: '', |
| 270 | paddingBottom: '', |
| 271 | paddingLeft: '', |
| 272 | paddingTopTablet: '', |
| 273 | paddingRightTablet: '', |
| 274 | paddingBottomTablet: '', |
| 275 | paddingLeftTablet: '', |
| 276 | paddingTopMobile: '', |
| 277 | paddingRightMobile: '', |
| 278 | paddingBottomMobile: '', |
| 279 | paddingLeftMobile: '', |
| 280 | marginLeft: ! isGrid && 'contained' === outerContainer ? 'auto' : spacing?.marginLeft, |
| 281 | marginRight: ! isGrid && 'contained' === outerContainer ? 'auto' : spacing?.marginRight, |
| 282 | }, |
| 283 | useGlobalMaxWidth: ! isGrid && 'contained' === outerContainer && !! hasDefaultContainerWidth, |
| 284 | sizing: { |
| 285 | height: isGrid ? '100%' : '', |
| 286 | maxWidth: ! isGrid && 'contained' === outerContainer && ! hasDefaultContainerWidth && containerWidth ? containerWidth + 'px' : '', |
| 287 | }, |
| 288 | ...layoutAttributes, |
| 289 | } ); |
| 290 | } |
| 291 | |
| 292 | /** |
| 293 | * Just updates current Container block attributes. |
| 294 | * Doesn't wrap inner blocks. |
| 295 | * |
| 296 | * @param {Object} props The function props. |
| 297 | */ |
| 298 | function doSimpleMigration( props ) { |
| 299 | const { |
| 300 | attributes, |
| 301 | setAttributes, |
| 302 | } = props; |
| 303 | |
| 304 | const { |
| 305 | isGrid, |
| 306 | outerContainer, |
| 307 | containerWidth, |
| 308 | spacing, |
| 309 | } = attributes; |
| 310 | |
| 311 | const hasDefaultContainerWidth = parseInt( containerWidth ) === parseInt( generateBlocksInfo.globalContainerWidth ); |
| 312 | const layoutAttributes = getLayoutAttributes( attributes ); |
| 313 | |
| 314 | setAttributes( { |
| 315 | useInnerContainer: false, |
| 316 | useGlobalMaxWidth: ! isGrid && 'contained' === outerContainer && !! hasDefaultContainerWidth, |
| 317 | spacing: { |
| 318 | marginLeft: ! isGrid && 'contained' === outerContainer ? 'auto' : spacing?.marginLeft, |
| 319 | marginRight: ! isGrid && 'contained' === outerContainer ? 'auto' : spacing?.marginRight, |
| 320 | }, |
| 321 | sizing: { |
| 322 | height: isGrid ? '100%' : '', |
| 323 | maxWidth: ! isGrid && 'contained' === outerContainer && ! hasDefaultContainerWidth && containerWidth ? containerWidth + 'px' : '', |
| 324 | }, |
| 325 | ...layoutAttributes, |
| 326 | } ); |
| 327 | } |
| 328 | |
| 329 | export { |
| 330 | shouldMigrateInnerContainer, |
| 331 | doInnerContainerMigration, |
| 332 | getLayoutAttributes, |
| 333 | doSimpleMigration, |
| 334 | }; |
| 335 |