PluginProbe
Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts / trunk
Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts vtrunk
2.8.14 2.8.13 2.8.12 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.10 2.8.11 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 trunk 1.0.1 1.0.2 1.1.0 1.1.1 All 147 releases
content-blocks-builder / src / features / grid / grid.js

grid.js in Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts trunk, at src/features/grid/grid.js

505 lines 15.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * External dependencies
3 */
4 import { isEmpty, isObject, isString, isUndefined } from "lodash";
5 import isDeepEqual from "fast-deep-equal/es6";
6
7 /**
8 * WordPress dependencies
9 */
10 import { __, sprintf } from "@wordpress/i18n";
11 import { addFilter, applyFilters } from "@wordpress/hooks";
12 import { createHigherOrderComponent } from "@wordpress/compose";
13 import {
14 TextareaControl,
15 __experimentalVStack as VStack,
16 __experimentalToolsPanel as ToolsPanel,
17 __experimentalToolsPanelItem as ToolsPanelItem,
18 } from "@wordpress/components";
19 import {
20 InspectorControls,
21 store as blockEditorStore,
22 } from "@wordpress/block-editor";
23 import { hasBlockSupport } from "@wordpress/blocks";
24 import { useSelect } from "@wordpress/data";
25
26 /**
27 * Internal dependencies
28 */
29 import { LabelControl } from "sdk/components";
30 import {
31 getBreakpointType,
32 getAllBreakpoints,
33 buildResponsiveSettingValue,
34 getValueByBreakpointFromResponsiveValue,
35 handleChangeResponsiveSettingGroupField,
36 getResponsiveSettingGroupFieldValue,
37 handleChangeSettingField,
38 getSettingFieldValue,
39 addAttributes,
40 addEditProps,
41 usePropsStyle,
42 } from "sdk/utils";
43 import { hasSupportLayout } from "../../utils";
44 import { labels } from "../../utils/labels";
45 import GapControl from "../../components/gap-control";
46 import LayoutPresetsControl from "./layout-presets-control";
47 import { gridSettingsDefault, layoutPresets, getGridGapStyle } from "./utils";
48 import { getGridGapStyleDeprecatedV1 } from "./deprecated";
49 import { HelpLink } from "../../components/help-link";
50
51 /**
52 * Define feature name
53 */
54 const layoutType = "layoutType";
55 const featureName = "grid";
56
57 /**
58 * Get styles for grid.
59 *
60 * @param {Object} grid
61 * @param {Boolean} hasDeprecatedV1
62 */
63 export function getGridStyles(grid, hasDeprecatedV1) {
64 let gridStyles = {};
65 if (!isEmpty(grid)) {
66 const { columns, rows, autoRows, gap } = grid;
67 if (isObject(columns) && !isEmpty(columns)) {
68 let columnStyle = {};
69 Object.keys(columns).forEach((breakpoint) => {
70 const { value, inherit } = columns[breakpoint];
71 if (isUndefined(value)) {
72 if (inherit && isString(inherit)) {
73 columnStyle = {
74 ...columnStyle,
75 [`--bb--grid--columns--${breakpoint}`]: `var(--bb--grid--columns--${inherit})`,
76 };
77 }
78 } else {
79 columnStyle = {
80 ...columnStyle,
81 [`--bb--grid--columns--${breakpoint}`]: value + "",
82 };
83 }
84 });
85
86 if (!isEmpty(columnStyle)) {
87 gridStyles = { ...gridStyles, ...columnStyle };
88 }
89 }
90
91 if (isObject(rows) && !isEmpty(rows)) {
92 let rowStyle = {};
93 Object.keys(rows).forEach((breakpoint) => {
94 const { value, inherit } = rows[breakpoint];
95 if (isUndefined(value)) {
96 if (inherit && isString(inherit)) {
97 rowStyle = {
98 ...rowStyle,
99 [`--bb--grid--rows--${breakpoint}`]: `var(--bb--grid--rows--${inherit})`,
100 };
101 }
102 } else {
103 rowStyle = {
104 ...rowStyle,
105 [`--bb--grid--rows--${breakpoint}`]: value + "",
106 };
107 }
108 });
109
110 if (!isEmpty(rowStyle)) {
111 gridStyles = { ...gridStyles, ...rowStyle };
112 }
113 }
114
115 if (isObject(autoRows) && !isEmpty(autoRows)) {
116 let autoRowsStyle = {};
117 Object.keys(autoRows).forEach((breakpoint) => {
118 const { value, inherit } = autoRows[breakpoint];
119 if (isUndefined(value)) {
120 if (inherit && isString(inherit)) {
121 autoRowsStyle = {
122 ...autoRowsStyle,
123 [`--bb--grid--auto-rows--${breakpoint}`]: `var(--bb--grid--auto-rows--${inherit})`,
124 };
125 }
126 } else {
127 autoRowsStyle = {
128 ...autoRowsStyle,
129 [`--bb--grid--auto-rows--${breakpoint}`]: value + "",
130 };
131 }
132 });
133
134 if (!isEmpty(autoRowsStyle)) {
135 gridStyles = { ...gridStyles, ...autoRowsStyle };
136 }
137 }
138
139 if (isObject(gap) && !isEmpty(gap)) {
140 let gapStyle = {};
141 if (hasDeprecatedV1) {
142 gapStyle = getGridGapStyleDeprecatedV1(gap);
143 } else {
144 gapStyle = getGridGapStyle(gap);
145 }
146
147 if (!isEmpty(gapStyle)) {
148 gridStyles = { ...gridStyles, ...gapStyle };
149 }
150 }
151 }
152
153 return gridStyles;
154 }
155
156 /**
157 * Add custom attributes.
158 *
159 * @param {Object} settings Settings for the block.
160 * @return {Object} settings Modified settings.
161 */
162 // pretter-ignore
163 addFilter(
164 "blocks.registerBlockType",
165 `boldblocks/${featureName}/addAttributes`,
166 (settings) => {
167 let defaultSettings = gridSettingsDefault;
168 // Don't add attributes to the post template bilock
169 if (settings?.name === "core/post-template") {
170 defaultSettings = {};
171 }
172
173 return addAttributes(
174 defaultSettings,
175 (settings, { featureName, layoutType }) =>
176 hasSupportLayout(settings, featureName, layoutType, {
177 context: "AddAttributes",
178 }),
179 { featureName, layoutType },
180 )(settings);
181 },
182 );
183
184 /**
185 * Override the default edit UI to include new inspector controls for custom settings.
186 *
187 * @param {Function} BlockEdit Block edit component.
188 * @return {Function} BlockEdit Modified block edit component.
189 */
190 const withInspectorControls = createHigherOrderComponent((BlockEdit) => {
191 return (props) => {
192 if (
193 !hasSupportLayout(props.name, featureName, layoutType, {
194 context: "BlockEdit",
195 })
196 ) {
197 return <BlockEdit {...props} />;
198 }
199
200 // Allow the ability to cancel the support.
201 if (!applyFilters("boldblocks.hasSupportGrid", true, { props })) {
202 return <BlockEdit {...props} />;
203 }
204
205 const { getBlock, getBlockParentsByBlockName } = useSelect((select) =>
206 select(blockEditorStore),
207 );
208
209 const { attributes, setAttributes, isSelected } = props;
210 const { boldblocks: { grid = {} } = {} } = attributes;
211
212 const changeGrid = handleChangeSettingField({
213 fieldName: "grid",
214 attributes,
215 setAttributes,
216 });
217
218 // Get all breakpoints
219 const allBreakpoints = getAllBreakpoints();
220
221 // Get current breakpoint
222 const breakpoint = getBreakpointType();
223
224 const getGridResponsiveGroupFieldValue = ({ fieldName, defaultValue }) =>
225 getResponsiveSettingGroupFieldValue({
226 fieldName,
227 groupName: "grid",
228 attributes,
229 breakpoint,
230 defaultValue,
231 });
232
233 const handleGridResponsiveGroupFieldChange = (fieldName) =>
234 handleChangeResponsiveSettingGroupField({
235 fieldName,
236 groupName: "grid",
237 setAttributes,
238 attributes,
239 breakpoint,
240 allBreakpoints,
241 });
242
243 // Get template columns.
244 const columnsByBreakpoint = getGridResponsiveGroupFieldValue({
245 fieldName: "columns",
246 defaultValue: "none",
247 });
248
249 // Get template rows.
250 const rowsByBreakpoint = getGridResponsiveGroupFieldValue({
251 fieldName: "rows",
252 defaultValue: "",
253 });
254
255 // Get auto rows.
256 const autoRowsByBreakpoint = getGridResponsiveGroupFieldValue({
257 fieldName: "autoRows",
258 defaultValue: "",
259 });
260
261 // Handle layout change
262 const handleLayoutChange = (layout) => {
263 const {
264 attributes: {
265 grid: { columns },
266 },
267 } = layout;
268
269 // Update grid columns.
270 handleGridResponsiveGroupFieldChange("columns")(columns);
271 };
272
273 const layoutLabel = __("Choose a layout", "content-blocks-builder");
274 const columnsLabel = __("Grid template columns", "content-blocks-builder");
275 const rowsLabel = __("Grid template rows", "content-blocks-builder");
276 const autoRowsLabel = __("Grid auto rows", "content-blocks-builder");
277 const gapLabel = __("Gap", "content-blocks-builder");
278
279 return (
280 <>
281 <BlockEdit {...props} />
282 {isSelected && (
283 <InspectorControls>
284 <ToolsPanel
285 label={__("Grid settings", "content-blocks-builder")}
286 resetAll={() => changeGrid(gridSettingsDefault)}
287 className="cbb-settings is-grid-settings"
288 >
289 <ToolsPanelItem
290 label={columnsLabel}
291 isShownByDefault
292 hasValue={() =>
293 !isDeepEqual(grid?.columns, gridSettingsDefault.grid.columns)
294 }
295 onDeselect={() =>
296 changeGrid({
297 ...grid,
298 columns: gridSettingsDefault.grid.columns,
299 })
300 }
301 >
302 <VStack spacing={3}>
303 <div>
304 {__(
305 "Choose a predefined layout or input a custom value for grid-template-columns.",
306 "content-blocks-builder",
307 )}
308 </div>
309 <LayoutPresetsControl
310 label={layoutLabel}
311 value={columnsByBreakpoint}
312 layouts={layoutPresets}
313 breakpoint={breakpoint}
314 onChange={handleLayoutChange}
315 />
316 <VStack spacing={0}>
317 <LabelControl label={columnsLabel} isResponsive={true} />
318 <TextareaControl
319 value={columnsByBreakpoint}
320 onChange={handleGridResponsiveGroupFieldChange("columns")}
321 rows={2}
322 help={
323 <HelpLink
324 href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns"
325 label={sprintf(
326 labels.learnS,
327 "grid-template-columns",
328 )}
329 />
330 }
331 />
332 </VStack>
333 </VStack>
334 </ToolsPanelItem>
335 <ToolsPanelItem
336 label={rowsLabel}
337 hasValue={() => !!grid?.rows}
338 onDeselect={() => {
339 const { rows, ...gridProps } = grid;
340 changeGrid(gridProps);
341 }}
342 >
343 <VStack spacing={0}>
344 <LabelControl label={rowsLabel} isResponsive={true} />
345 <TextareaControl
346 value={rowsByBreakpoint}
347 onChange={handleGridResponsiveGroupFieldChange("rows")}
348 rows={2}
349 placeholder="auto"
350 help={
351 <HelpLink
352 href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-rows"
353 label={sprintf(labels.learnS, "grid-template-rows")}
354 />
355 }
356 />
357 </VStack>
358 </ToolsPanelItem>
359 <ToolsPanelItem
360 label={autoRowsLabel}
361 hasValue={() => !!grid?.autoRows}
362 onDeselect={() => {
363 const { autoRows, ...gridProps } = grid;
364 changeGrid(gridProps);
365 }}
366 >
367 <VStack spacing={0}>
368 <LabelControl label={autoRowsLabel} isResponsive={true} />
369 <TextareaControl
370 value={autoRowsByBreakpoint}
371 onChange={handleGridResponsiveGroupFieldChange("autoRows")}
372 rows={2}
373 placeholder="auto"
374 help={
375 <HelpLink
376 href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-rows"
377 label={sprintf(labels.learnS, "grid-auto-rows")}
378 />
379 }
380 />
381 </VStack>
382 </ToolsPanelItem>
383 <ToolsPanelItem
384 label={gapLabel}
385 isShownByDefault
386 hasValue={() =>
387 !isDeepEqual(grid?.gap, gridSettingsDefault.grid.gap)
388 }
389 onDeselect={() =>
390 changeGrid({ ...grid, gap: gridSettingsDefault.grid.gap })
391 }
392 >
393 <GapControl
394 label={gapLabel}
395 values={getGridResponsiveGroupFieldValue({
396 fieldName: "gap",
397 defaultValue: {},
398 })}
399 onChange={handleGridResponsiveGroupFieldChange("gap")}
400 />
401 </ToolsPanelItem>
402 </ToolsPanel>
403 {applyFilters("boldblocks.grid.additionalSettings", null, {
404 props,
405 breakpoint,
406 allBreakpoints,
407 buildResponsiveSettingValue,
408 getValueByBreakpointFromResponsiveValue,
409 handleGridSettingFieldChange: (fieldName) =>
410 handleChangeSettingField({
411 fieldName,
412 setAttributes,
413 attributes,
414 }),
415 getGridSettingFieldValue: ({ fieldName, defaultValue }) =>
416 getSettingFieldValue({ fieldName, defaultValue, attributes }),
417 getBlock,
418 getBlockParentsByBlockName,
419 })}
420 </InspectorControls>
421 )}
422 </>
423 );
424 };
425 }, "withInspectorControls");
426 addFilter(
427 "editor.BlockEdit",
428 `boldblocks/${featureName}/withInspectorControls`,
429 withInspectorControls,
430 );
431
432 /**
433 * Override props assigned to save component to inject the CSS variables definition.
434 *
435 * @param {Object} props Additional props applied to save element.
436 * @param {Object} blockType Block type.
437 * @param {Object} attributes Block attributes.
438 *
439 * @return {Object} Filtered props applied to save element.
440 */
441 export function addSaveProps(props, blockType, attributes) {
442 if (
443 !hasSupportLayout(blockType, featureName, layoutType, {
444 context: "EditProps",
445 })
446 ) {
447 return props;
448 }
449
450 const { boldblocks: { grid = {} } = {} } = attributes;
451
452 const hasDeprecatedV1 = hasBlockSupport(
453 blockType,
454 "CBBDeprecatedGridStyleV1",
455 );
456
457 const isDeprecatedDynamicStyle = hasBlockSupport(
458 blockType,
459 "CBBDeprecatedDynamicStyleV1",
460 );
461
462 let featureStyles = {};
463 if (attributes?.isBlockEdit) {
464 featureStyles = usePropsStyle({
465 value: grid,
466 getStyle: (value) => () => getGridStyles(value, hasDeprecatedV1),
467 });
468 } else {
469 if (isDeprecatedDynamicStyle) {
470 featureStyles = getGridStyles(grid, hasDeprecatedV1);
471 }
472 }
473
474 props.style = {
475 ...props.style,
476 ...featureStyles,
477 };
478
479 return props;
480 }
481 addFilter(
482 "blocks.getSaveContent.extraProps",
483 `boldblocks/${featureName}/addSaveProps`,
484 addSaveProps,
485 );
486
487 /**
488 * Filters registered block settings to extend the block edit wrapper
489 * to apply the desired styles and className properly.
490 *
491 * @param {Object} settings Original block settings.
492 *
493 * @return {Object}.Filtered block settings.
494 */
495 addFilter(
496 "blocks.registerBlockType",
497 `boldblocks/${featureName}/addEditProps`,
498 addEditProps(
499 addSaveProps,
500 (settings, { featureName, layoutType }) =>
501 hasSupportLayout(settings, featureName, layoutType),
502 { featureName, layoutType },
503 ),
504 );
505