@wordpress/block-library
Version:
Block library for the WordPress editor.
453 lines (402 loc) • 15.1 kB
JavaScript
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _element = require("@wordpress/element");
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _reactNative = require("react-native");
var _lodash = require("lodash");
var _i18n = require("@wordpress/i18n");
var _components = require("@wordpress/components");
var _blockEditor = require("@wordpress/block-editor");
var _data = require("@wordpress/data");
var _compose = require("@wordpress/compose");
var _blocks = require("@wordpress/blocks");
var _icons = require("@wordpress/icons");
var _variations = _interopRequireDefault(require("./variations"));
var _editor = _interopRequireDefault(require("./editor.scss"));
var _utils = require("./utils");
var _columnCalculations = require("./columnCalculations.native");
var _columnPreview = _interopRequireDefault(require("../column/column-preview"));
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
/**
* Allowed blocks constant is passed to InnerBlocks precisely as specified here.
* The contents of the array should never change.
* The array should contain the name of each block that is allowed.
* In columns block, the only block we allow is 'core/column'.
*
* @constant
* @type {string[]}
*/
const ALLOWED_BLOCKS = ['core/column'];
/**
* Number of columns to assume for template in case the user opts to skip
* template option selection.
*
* @type {number}
*/
const DEFAULT_COLUMNS_NUM = 2;
/**
* Minimum number of columns in a row
*
* @type {number}
*/
const MIN_COLUMNS_NUM = 1;
const {
isFullWidth
} = _components.alignmentHelpers;
function ColumnsEditContainer(_ref) {
let {
attributes,
updateAlignment,
updateColumns,
columnCount,
isSelected,
onDeleteBlock,
innerWidths,
updateInnerColumnWidth,
editorSidebarOpened
} = _ref;
const [resizeListener, sizes] = (0, _compose.useResizeObserver)();
const [columnsInRow, setColumnsInRow] = (0, _element.useState)(MIN_COLUMNS_NUM);
const screenWidth = Math.floor(_reactNative.Dimensions.get('window').width);
const globalStyles = (0, _element.useContext)(_components.GlobalStylesContext);
const {
verticalAlignment,
align
} = attributes;
const {
width
} = sizes || {};
const units = (0, _components.__experimentalUseCustomUnits)({
availableUnits: (0, _blockEditor.useSetting)('spacing.units') || ['%', 'px', 'em', 'rem', 'vw']
});
(0, _element.useEffect)(() => {
if (columnCount === 0) {
const newColumnCount = columnCount || DEFAULT_COLUMNS_NUM;
updateColumns(columnCount, newColumnCount);
}
}, []);
(0, _element.useEffect)(() => {
if (width) {
if ((0, _columnCalculations.getColumnsInRow)(width, columnCount) !== columnsInRow) {
setColumnsInRow((0, _columnCalculations.getColumnsInRow)(width, columnCount));
}
}
}, [width, columnCount]);
const renderAppender = () => {
if (isSelected) {
return (0, _element.createElement)(_reactNative.View, {
style: isFullWidth(align) && _editor.default.columnAppender
}, (0, _element.createElement)(_blockEditor.InnerBlocks.ButtonBlockAppender, {
onAddBlock: onAddBlock
}));
}
return null;
};
const contentWidths = (0, _element.useMemo)(() => (0, _columnCalculations.getContentWidths)(columnsInRow, width, columnCount, innerWidths, globalStyles), [width, columnsInRow, columnCount, innerWidths, globalStyles]);
const onAddBlock = (0, _element.useCallback)(() => {
updateColumns(columnCount, columnCount + 1);
}, [columnCount]);
const onChangeWidth = (nextWidth, valueUnit, columnId) => {
const widthWithUnit = (0, _utils.getWidthWithUnit)(nextWidth, valueUnit);
updateInnerColumnWidth(widthWithUnit, columnId);
};
const onChangeUnit = (nextUnit, index, columnId) => {
const widthWithoutUnit = parseFloat((0, _utils.getWidths)(innerWidths)[index]);
const widthWithUnit = (0, _utils.getWidthWithUnit)(widthWithoutUnit, nextUnit);
updateInnerColumnWidth(widthWithUnit, columnId);
};
const onChange = (nextWidth, valueUnit, columnId) => {
if ((0, _utils.isPercentageUnit)(valueUnit) || !valueUnit) {
return;
}
onChangeWidth(nextWidth, valueUnit, columnId);
};
const getColumnsSliders = (0, _element.useMemo)(() => {
if (!editorSidebarOpened || !isSelected) {
return null;
}
return innerWidths.map((column, index) => {
const {
valueUnit = '%'
} = (0, _components.getValueAndUnit)(column.attributes.width) || {};
const label = (0, _i18n.sprintf)(
/* translators: %d: column index. */
(0, _i18n.__)('Column %d'), index + 1);
return (0, _element.createElement)(_components.UnitControl, {
label: label,
settingLabel: "Width",
key: `${column.clientId}-${(0, _utils.getWidths)(innerWidths).length}`,
min: 1,
max: (0, _utils.isPercentageUnit)(valueUnit) || !valueUnit ? 100 : undefined,
value: (0, _utils.getWidths)(innerWidths)[index],
onChange: nextWidth => {
onChange(nextWidth, valueUnit, column.clientId);
},
onUnitChange: nextUnit => onChangeUnit(nextUnit, index, column.clientId),
onComplete: nextWidth => {
onChangeWidth(nextWidth, valueUnit, column.clientId);
},
unit: valueUnit,
units: units,
preview: (0, _element.createElement)(_columnPreview.default, {
columnWidths: (0, _utils.getWidths)(innerWidths, false),
selectedColumnIndex: index
})
});
});
}, [editorSidebarOpened, isSelected, innerWidths]);
const onChangeColumnsNum = (0, _element.useCallback)(value => {
updateColumns(columnCount, value);
}, [columnCount]);
return (0, _element.createElement)(_element.Fragment, null, isSelected && (0, _element.createElement)(_element.Fragment, null, (0, _element.createElement)(_blockEditor.InspectorControls, null, (0, _element.createElement)(_components.PanelBody, {
title: (0, _i18n.__)('Columns Settings')
}, (0, _element.createElement)(_components.RangeControl, {
label: (0, _i18n.__)('Number of columns'),
icon: _icons.columns,
value: columnCount,
onChange: onChangeColumnsNum,
min: MIN_COLUMNS_NUM,
max: columnCount + 1,
type: "stepper"
}), getColumnsSliders), (0, _element.createElement)(_components.PanelBody, null, (0, _element.createElement)(_components.FooterMessageControl, {
label: (0, _i18n.__)('Note: Column layout may vary between themes and screen sizes')
}))), (0, _element.createElement)(_blockEditor.BlockControls, null, (0, _element.createElement)(_blockEditor.BlockVerticalAlignmentToolbar, {
onChange: updateAlignment,
value: verticalAlignment
}))), (0, _element.createElement)(_reactNative.View, {
style: isSelected && _editor.default.innerBlocksSelected
}, resizeListener, width && (0, _element.createElement)(_blockEditor.InnerBlocks, {
renderAppender: renderAppender,
orientation: columnsInRow > 1 ? 'horizontal' : undefined,
horizontal: columnsInRow > 1,
allowedBlocks: ALLOWED_BLOCKS,
contentResizeMode: "stretch",
onAddBlock: onAddBlock,
onDeleteBlock: columnCount === 1 ? onDeleteBlock : undefined,
blockWidth: width,
contentStyle: contentWidths,
parentWidth: isFullWidth(align) && columnCount === 0 ? screenWidth : (0, _columnCalculations.calculateContainerWidth)(width, columnsInRow)
})));
}
const ColumnsEditContainerWrapper = (0, _data.withDispatch)((dispatch, ownProps, registry) => ({
/**
* Update all child Column blocks with a new vertical alignment setting
* based on whatever alignment is passed in. This allows change to parent
* to overide anything set on a individual column basis.
*
* @param {string} verticalAlignment the vertical alignment setting
*/
updateAlignment(verticalAlignment) {
const {
clientId,
setAttributes
} = ownProps;
const {
updateBlockAttributes
} = dispatch(_blockEditor.store);
const {
getBlockOrder
} = registry.select(_blockEditor.store); // Update own alignment.
setAttributes({
verticalAlignment
}); // Update all child Column Blocks to match.
const innerBlockClientIds = getBlockOrder(clientId);
innerBlockClientIds.forEach(innerBlockClientId => {
updateBlockAttributes(innerBlockClientId, {
verticalAlignment
});
});
},
updateInnerColumnWidth(value, columnId) {
const {
updateBlockAttributes
} = dispatch(_blockEditor.store);
updateBlockAttributes(columnId, {
width: value
});
},
updateBlockSettings(settings) {
const {
clientId
} = ownProps;
const {
updateBlockListSettings
} = dispatch(_blockEditor.store);
updateBlockListSettings(clientId, settings);
},
/**
* Updates the column columnCount, including necessary revisions to child Column
* blocks to grant required or redistribute available space.
*
* @param {number} previousColumns Previous column columnCount.
* @param {number} newColumns New column columnCount.
*/
updateColumns(previousColumns, newColumns) {
const {
clientId
} = ownProps;
const {
replaceInnerBlocks
} = dispatch(_blockEditor.store);
const {
getBlocks,
getBlockAttributes
} = registry.select(_blockEditor.store);
let innerBlocks = getBlocks(clientId);
const hasExplicitWidths = (0, _utils.hasExplicitPercentColumnWidths)(innerBlocks); // Redistribute available width for existing inner blocks.
const isAddingColumn = newColumns > previousColumns; // Get verticalAlignment from Columns block to set the same to new Column.
const {
verticalAlignment
} = getBlockAttributes(clientId) || {};
if (isAddingColumn && hasExplicitWidths) {
// If adding a new column, assign width to the new column equal to
// as if it were `1 / columns` of the total available space.
const newColumnWidth = (0, _utils.toWidthPrecision)(100 / newColumns); // Redistribute in consideration of pending block insertion as
// constraining the available working width.
const widths = (0, _utils.getRedistributedColumnWidths)(innerBlocks, 100 - newColumnWidth);
innerBlocks = [...(0, _utils.getMappedColumnWidths)(innerBlocks, widths), ...Array.from({
length: newColumns - previousColumns
}).map(() => {
return (0, _blocks.createBlock)('core/column', {
width: `${newColumnWidth}%`,
verticalAlignment
});
})];
} else if (isAddingColumn) {
innerBlocks = [...innerBlocks, ...Array.from({
length: newColumns - previousColumns
}).map(() => {
return (0, _blocks.createBlock)('core/column', {
verticalAlignment
});
})];
} else {
// The removed column will be the last of the inner blocks.
innerBlocks = innerBlocks.slice(0, -(previousColumns - newColumns));
if (hasExplicitWidths) {
// Redistribute as if block is already removed.
const widths = (0, _utils.getRedistributedColumnWidths)(innerBlocks, 100);
innerBlocks = (0, _utils.getMappedColumnWidths)(innerBlocks, widths);
}
}
replaceInnerBlocks(clientId, innerBlocks);
},
onAddNextColumn: () => {
const {
clientId
} = ownProps;
const {
replaceInnerBlocks,
selectBlock
} = dispatch(_blockEditor.store);
const {
getBlocks,
getBlockAttributes
} = registry.select(_blockEditor.store); // Get verticalAlignment from Columns block to set the same to new Column.
const {
verticalAlignment
} = getBlockAttributes(clientId);
const innerBlocks = getBlocks(clientId);
const insertedBlock = (0, _blocks.createBlock)('core/column', {
verticalAlignment
});
replaceInnerBlocks(clientId, [...innerBlocks, insertedBlock], true);
selectBlock(insertedBlock.clientId);
},
onDeleteBlock: () => {
const {
clientId
} = ownProps;
const {
removeBlock
} = dispatch(_blockEditor.store);
removeBlock(clientId);
}
}))((0, _element.memo)(ColumnsEditContainer));
const ColumnsEdit = props => {
const {
clientId,
isSelected,
style
} = props;
const {
columnCount,
isDefaultColumns,
innerWidths = [],
hasParents,
parentBlockAlignment,
editorSidebarOpened
} = (0, _data.useSelect)(select => {
var _getBlockAttributes;
const {
getBlockCount,
getBlocks,
getBlockParents,
getBlockAttributes
} = select(_blockEditor.store);
const {
isEditorSidebarOpened
} = select('core/edit-post');
const innerBlocks = getBlocks(clientId);
const isContentEmpty = (0, _lodash.map)(innerBlocks, innerBlock => innerBlock.innerBlocks.length);
const innerColumnsWidths = innerBlocks.map(inn => ({
clientId: inn.clientId,
attributes: {
width: inn.attributes.width
}
}));
const parents = getBlockParents(clientId, true);
return {
columnCount: getBlockCount(clientId),
isDefaultColumns: !isContentEmpty.filter(Boolean).length,
innerWidths: innerColumnsWidths,
hasParents: !!parents.length,
parentBlockAlignment: (_getBlockAttributes = getBlockAttributes(parents[0])) === null || _getBlockAttributes === void 0 ? void 0 : _getBlockAttributes.align,
editorSidebarOpened: isSelected && isEditorSidebarOpened()
};
}, [clientId, isSelected]);
const memoizedInnerWidths = (0, _element.useMemo)(() => {
return innerWidths;
}, [// The JSON.stringify is used because innerWidth is always a new reference.
// The innerBlocks is a new reference after each attribute change of any nested block.
JSON.stringify(innerWidths)]);
const [isVisible, setIsVisible] = (0, _element.useState)(false);
(0, _element.useEffect)(() => {
if (isSelected && isDefaultColumns) {
const revealTimeout = setTimeout(() => setIsVisible(true), 100);
return () => clearTimeout(revealTimeout);
}
}, []);
const onClose = (0, _element.useCallback)(() => {
setIsVisible(false);
}, []);
return (0, _element.createElement)(_reactNative.View, {
style: style
}, (0, _element.createElement)(ColumnsEditContainerWrapper, (0, _extends2.default)({
columnCount: columnCount,
innerWidths: memoizedInnerWidths,
hasParents: hasParents,
parentBlockAlignment: parentBlockAlignment,
editorSidebarOpened: editorSidebarOpened
}, props)), (0, _element.createElement)(_blockEditor.BlockVariationPicker, {
variations: _variations.default,
onClose: onClose,
clientId: clientId,
isVisible: isVisible
}));
};
var _default = ColumnsEdit;
exports.default = _default;
//# sourceMappingURL=edit.native.js.map
;