UNPKG

@wordpress/blocks

Version:
261 lines (227 loc) 8.66 kB
/** * External dependencies */ import { every, has, isFunction, isString, reduce } from 'lodash'; import { default as tinycolor, mostReadable } from 'tinycolor2'; /** * WordPress dependencies */ import { Component, isValidElement } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; /** * Internal dependencies */ import { getBlockType, getDefaultBlockName } from './registration'; import { createBlock } from './factory'; /** * Array of icon colors containing a color to be used if the icon color * was not explicitly set but the icon background color was. * * @type {Object} */ const ICON_COLORS = ['#191e23', '#f8f9f9']; /** * Determines whether the block is a default block * and its attributes are equal to the default attributes * which means the block is unmodified. * * @param {WPBlock} block Block Object * * @return {boolean} Whether the block is an unmodified default block */ export function isUnmodifiedDefaultBlock(block) { const defaultBlockName = getDefaultBlockName(); if (block.name !== defaultBlockName) { return false; } // Cache a created default block if no cache exists or the default block // name changed. if (!isUnmodifiedDefaultBlock.block || isUnmodifiedDefaultBlock.block.name !== defaultBlockName) { isUnmodifiedDefaultBlock.block = createBlock(defaultBlockName); } const newDefaultBlock = isUnmodifiedDefaultBlock.block; const blockType = getBlockType(defaultBlockName); return every(blockType.attributes, (value, key) => newDefaultBlock.attributes[key] === block.attributes[key]); } /** * Function that checks if the parameter is a valid icon. * * @param {*} icon Parameter to be checked. * * @return {boolean} True if the parameter is a valid icon and false otherwise. */ export function isValidIcon(icon) { return !!icon && (isString(icon) || isValidElement(icon) || isFunction(icon) || icon instanceof Component); } /** * Function that receives an icon as set by the blocks during the registration * and returns a new icon object that is normalized so we can rely on just on possible icon structure * in the codebase. * * @param {WPBlockTypeIconRender} icon Render behavior of a block type icon; * one of a Dashicon slug, an element, or a * component. * * @return {WPBlockTypeIconDescriptor} Object describing the icon. */ export function normalizeIconObject(icon) { if (isValidIcon(icon)) { return { src: icon }; } if (has(icon, ['background'])) { const tinyBgColor = tinycolor(icon.background); return { ...icon, foreground: icon.foreground ? icon.foreground : mostReadable(tinyBgColor, ICON_COLORS, { includeFallbackColors: true, level: 'AA', size: 'large' }).toHexString(), shadowColor: tinyBgColor.setAlpha(0.3).toRgbString() }; } return icon; } /** * Normalizes block type passed as param. When string is passed then * it converts it to the matching block type object. * It passes the original object otherwise. * * @param {string|Object} blockTypeOrName Block type or name. * * @return {?Object} Block type. */ export function normalizeBlockType(blockTypeOrName) { if (isString(blockTypeOrName)) { return getBlockType(blockTypeOrName); } return blockTypeOrName; } /** * Get the label for the block, usually this is either the block title, * or the value of the block's `label` function when that's specified. * * @param {Object} blockType The block type. * @param {Object} attributes The values of the block's attributes. * @param {Object} context The intended use for the label. * * @return {string} The block label. */ export function getBlockLabel(blockType, attributes, context = 'visual') { const { __experimentalLabel: getLabel, title } = blockType; const label = getLabel && getLabel(attributes, { context }); if (!label) { return title; } // Strip any HTML (i.e. RichText formatting) before returning. return stripHTML(label); } /** * Get a label for the block for use by screenreaders, this is more descriptive * than the visual label and includes the block title and the value of the * `getLabel` function if it's specified. * * @param {Object} blockType The block type. * @param {Object} attributes The values of the block's attributes. * @param {?number} position The position of the block in the block list. * @param {string} [direction='vertical'] The direction of the block layout. * * @return {string} The block label. */ export function getAccessibleBlockLabel(blockType, attributes, position, direction = 'vertical') { // `title` is already localized, `label` is a user-supplied value. const { title } = blockType; const label = getBlockLabel(blockType, attributes, 'accessibility'); const hasPosition = position !== undefined; // getBlockLabel returns the block title as a fallback when there's no label, // if it did return the title, this function needs to avoid adding the // title twice within the accessible label. Use this `hasLabel` boolean to // handle that. const hasLabel = label && label !== title; if (hasPosition && direction === 'vertical') { if (hasLabel) { return sprintf( /* translators: accessibility text. 1: The block title. 2: The block row number. 3: The block label.. */ __('%1$s Block. Row %2$d. %3$s'), title, position, label); } return sprintf( /* translators: accessibility text. 1: The block title. 2: The block row number. */ __('%1$s Block. Row %2$d'), title, position); } else if (hasPosition && direction === 'horizontal') { if (hasLabel) { return sprintf( /* translators: accessibility text. 1: The block title. 2: The block column number. 3: The block label.. */ __('%1$s Block. Column %2$d. %3$s'), title, position, label); } return sprintf( /* translators: accessibility text. 1: The block title. 2: The block column number. */ __('%1$s Block. Column %2$d'), title, position); } if (hasLabel) { return sprintf( /* translators: accessibility text. %1: The block title. %2: The block label. */ __('%1$s Block. %2$s'), title, label); } return sprintf( /* translators: accessibility text. %s: The block title. */ __('%s Block'), title); } /** * Ensure attributes contains only values defined by block type, and merge * default values for missing attributes. * * @param {string} name The block's name. * @param {Object} attributes The block's attributes. * @return {Object} The sanitized attributes. */ export function __experimentalSanitizeBlockAttributes(name, attributes) { // Get the type definition associated with a registered block. const blockType = getBlockType(name); if (undefined === blockType) { throw new Error(`Block type '${name}' is not registered.`); } return reduce(blockType.attributes, (accumulator, schema, key) => { const value = attributes[key]; if (undefined !== value) { accumulator[key] = value; } else if (schema.hasOwnProperty('default')) { accumulator[key] = schema.default; } if (['node', 'children'].indexOf(schema.source) !== -1) { // Ensure value passed is always an array, which we're expecting in // the RichText component to handle the deprecated value. if (typeof accumulator[key] === 'string') { accumulator[key] = [accumulator[key]]; } else if (!Array.isArray(accumulator[key])) { accumulator[key] = []; } } return accumulator; }, {}); } /** * Filter block attributes by `role` and return their names. * * @param {string} name Block attribute's name. * @param {string} role The role of a block attribute. * * @return {string[]} The attribute names that have the provided role. */ export function __experimentalGetBlockAttributesNamesByRole(name, role) { var _getBlockType; const attributes = (_getBlockType = getBlockType(name)) === null || _getBlockType === void 0 ? void 0 : _getBlockType.attributes; if (!attributes) return []; const attributesNames = Object.keys(attributes); if (!role) return attributesNames; return attributesNames.filter(attributeName => { var _attributes$attribute; return ((_attributes$attribute = attributes[attributeName]) === null || _attributes$attribute === void 0 ? void 0 : _attributes$attribute.__experimentalRole) === role; }); } //# sourceMappingURL=utils.js.map