PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.14
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.14
2.0.14 2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
← All changes | blocks/row/index.js +210 -92 2.0.2 → 2.0.14 View file →
@@ -1,5 +1,5 @@
1 -wp.domReady(function () {
1 +( function () {
2 2 var el = wp.element.createElement;
3 3 var useState = wp.element.useState;
4 4 var useRef = wp.element.useRef;
5 5 var useEffect = wp.element.useEffect;
@@ -22,26 +22,12 @@
22 22 var ToolbarDropdownMenu = wp.components.ToolbarDropdownMenu;
23 23 var Button = wp.components.Button;
24 24 var ButtonGroup = wp.components.ButtonGroup;
25 25 var Placeholder = wp.components.Placeholder;
26 + var Popover = wp.components.Popover;
26 27
27 - // Gap token scale
28 - var gapOptions = [
29 - { label: __('None', 'blockenberg'), value: 'none' },
30 - { label: __('XS (8px)', 'blockenberg'), value: 'xs' },
31 - { label: __('S (16px)', 'blockenberg'), value: 's' },
32 - { label: __('M (24px)', 'blockenberg'), value: 'm' },
33 - { label: __('L (32px)', 'blockenberg'), value: 'l' },
34 - { label: __('XL (48px)', 'blockenberg'), value: 'xl' }
35 - ];
28 + // Gap token scale (legacy support in saved attributes/classes)
36 29
37 - // Vertical alignment options
38 - var vAlignOptions = [
39 - { label: __('Top', 'blockenberg'), value: 'top' },
40 - { label: __('Middle', 'blockenberg'), value: 'middle' },
41 - { label: __('Bottom', 'blockenberg'), value: 'bottom' }
42 - ];
43 -
44 30 // Responsive mode options
45 31 var responsiveModes = [
46 32 { value: 'desktop', label: __('Desktop', 'blockenberg'), icon: 'desktop' },
47 33 { value: 'tablet', label: __('Tablet', 'blockenberg'), icon: 'tablet' },
@@ -47,8 +33,48 @@
47 33 { value: 'tablet', label: __('Tablet', 'blockenberg'), icon: 'tablet' },
48 34 { value: 'mobile', label: __('Mobile', 'blockenberg'), icon: 'smartphone' }
49 35 ];
50 36
37 + var spacingUnitOptions = [
38 + { label: 'px', value: 'px' },
39 + { label: '%', value: '%' },
40 + { label: 'em', value: 'em' },
41 + { label: 'rem', value: 'rem' },
42 + { label: 'vw', value: 'vw' },
43 + { label: 'vh', value: 'vh' },
44 + { label: __('Custom', 'blockenberg'), value: 'custom' }
45 + ];
46 +
47 + function getSpacingSideValue(attrs, type, side) {
48 + var cap = side.charAt(0).toUpperCase() + side.slice(1);
49 + var value = attrs[type + cap];
50 + var unit = attrs[type + cap + 'Unit'] || 'px';
51 + var customUnit = attrs[type + cap + 'CustomUnit'] || '';
52 +
53 + if (value === undefined || value === null) return undefined;
54 + var raw = String(value).trim();
55 + if (!raw) return undefined;
56 +
57 + if (unit === 'custom') {
58 + return customUnit ? raw + customUnit : raw;
59 + }
60 +
61 + return raw + unit;
62 + }
63 +
64 + function getSpacingStyles(attrs) {
65 + return {
66 + paddingTop: getSpacingSideValue(attrs, 'padding', 'top'),
67 + paddingRight: getSpacingSideValue(attrs, 'padding', 'right'),
68 + paddingBottom: getSpacingSideValue(attrs, 'padding', 'bottom'),
69 + paddingLeft: getSpacingSideValue(attrs, 'padding', 'left'),
70 + marginTop: getSpacingSideValue(attrs, 'margin', 'top'),
71 + marginRight: getSpacingSideValue(attrs, 'margin', 'right'),
72 + marginBottom: getSpacingSideValue(attrs, 'margin', 'bottom'),
73 + marginLeft: getSpacingSideValue(attrs, 'margin', 'left')
74 + };
75 + }
76 +
51 77 // Column presets for quick setup
52 78 var columnPresets = [
53 79 { label: '1 Column (100)', value: '1-full', widths: [100], icon: '⬜' },
54 80 { label: '2 Columns (50/50)', value: '2-equal', widths: [50, 50], icon: '⬜⬜' },
@@ -266,9 +292,9 @@
266 292
267 293 registerBlockType('blockenberg/row', {
268 294 title: __('Row', 'blockenberg'),
269 295 icon: rowIcon,
270 - category: 'blockenberg',
296 + category: 'bkbg-layout',
271 297 parent: ['blockenberg/section'],
272 298 description: __('A row container for columns with drag-resize support.', 'blockenberg'),
273 299
274 300 edit: function (props) {
@@ -284,8 +310,12 @@
284 310 var hasSelectedLayoutState = useState(false);
285 311 var hasSelectedLayout = hasSelectedLayoutState[0];
286 312 var setHasSelectedLayout = hasSelectedLayoutState[1];
287 313
314 + var isStructureOpenState = useState(false);
315 + var isStructureOpen = isStructureOpenState[0];
316 + var setIsStructureOpen = isStructureOpenState[1];
317 +
288 318 var updateBlockAttributes = useDispatch('core/block-editor').updateBlockAttributes;
289 319 var selectBlock = useDispatch('core/block-editor').selectBlock;
290 320 var insertBlock = useDispatch('core/block-editor').insertBlock;
291 321 var replaceInnerBlocks = useDispatch('core/block-editor').replaceInnerBlocks;
@@ -402,11 +432,35 @@
402 432 return getColumnWidth(col, a.responsiveMode);
403 433 });
404 434 }
405 435
436 + function setWidthLabelsVisibilityDuringResize(show) {
437 + var selector = '[data-block="' + clientId + '"] .bkbg-column__width-label';
438 +
439 + function updateInDocument(doc) {
440 + if (!doc || !doc.querySelectorAll) return;
441 + var labels = doc.querySelectorAll(selector);
442 + labels.forEach(function (label) {
443 + if (show) {
444 + label.style.opacity = '1';
445 + } else {
446 + label.style.opacity = '';
447 + }
448 + });
449 + }
450 +
451 + updateInDocument(document);
452 +
453 + var editorCanvas = document.querySelector('iframe[name="editor-canvas"]');
454 + if (editorCanvas && editorCanvas.contentDocument) {
455 + updateInDocument(editorCanvas.contentDocument);
456 + }
457 + }
458 +
406 459 // Handle resize start
407 460 function handleResizeStart() {
408 461 setIsResizing(true);
462 + setWidthLabelsVisibilityDuringResize(true);
409 463 }
410 464
411 465 // Handle resize - update attributes immediately
412 466 function handleResize(leftIndex, newLeftPct, newRightPct) {
@@ -429,8 +483,9 @@
429 483
430 484 // Handle resize end
431 485 function handleResizeEnd() {
432 486 setIsResizing(false);
487 + setWidthLabelsVisibilityDuringResize(false);
433 488 // Ensure CSS class is also cleaned up
434 489 cleanupAllResizeStates();
435 490 }
436 491
@@ -459,8 +514,10 @@
459 514 widths: { desktop: width, tablet: width, mobile: 100 },
460 515 uid: existingCol.attributes.uid || ('col-' + Date.now() + '-' + i),
461 516 paddingInner: existingCol.attributes.paddingInner || 'none'
462 517 }, existingCol.innerBlocks);
518 +
519 + newCol.attributes = Object.assign({}, existingCol.attributes || {}, newCol.attributes || {});
463 520
464 521 newColumns.push(newCol);
465 522 }
466 523
@@ -537,8 +594,10 @@
537 594 widths: { desktop: width, tablet: width, mobile: 100 },
538 595 uid: existingCol.attributes.uid || ('col-' + Date.now() + '-' + colIndex),
539 596 paddingInner: existingCol.attributes.paddingInner || 'none'
540 597 }, existingCol.innerBlocks);
598 +
599 + recreatedCol.attributes = Object.assign({}, existingCol.attributes || {}, recreatedCol.attributes || {});
541 600 newColumns.push(recreatedCol);
542 601 colIndex++;
543 602 }
544 603 }
@@ -593,8 +652,10 @@
593 652 widths: { desktop: width, tablet: width, mobile: 100 },
594 653 uid: existingCol.attributes.uid || ('col-' + Date.now() + '-' + newIndex),
595 654 paddingInner: existingCol.attributes.paddingInner || 'none'
596 655 }, existingCol.innerBlocks);
656 +
657 + recreatedCol.attributes = Object.assign({}, existingCol.attributes || {}, recreatedCol.attributes || {});
597 658 newColumns.push(recreatedCol);
598 659 newIndex++;
599 660 }
600 661
@@ -694,16 +755,75 @@
694 755 var blockProps = useBlockProps({
695 756 className: [
696 757 'bkbg-row',
697 758 'bkbg-row--gap-' + a.gap,
698 - 'bkbg-row--valign-' + a.vAlign,
699 759 'bkbg-row--mode-' + a.responsiveMode,
700 760 a.stackOnMobile ? 'bkbg-row--stack-mobile' : '',
701 761 isResizing ? 'bkbg-row--is-resizing' : ''
702 762 ].filter(Boolean).join(' '),
763 + style: getSpacingStyles(a),
703 764 'data-responsive-mode': a.responsiveMode
704 765 });
705 766
767 + function renderSpacingControl(type, label) {
768 + var sides = [
769 + { key: 'Top', label: __('Top', 'blockenberg') },
770 + { key: 'Left', label: __('Left', 'blockenberg') },
771 + { key: 'Bottom', label: __('Bottom', 'blockenberg') },
772 + { key: 'Right', label: __('Right', 'blockenberg') }
773 + ];
774 +
775 + return el('div', { className: 'bkbg-spacing-control' },
776 + el('div', { className: 'bkbg-spacing-control__title' }, label),
777 + el('div', { className: 'bkbg-spacing-control__grid' },
778 + sides.map(function (side) {
779 + var valueKey = type + side.key;
780 + var unitKey = type + side.key + 'Unit';
781 + var customUnitKey = type + side.key + 'CustomUnit';
782 +
783 + return el('div', { className: 'bkbg-spacing-control__item', key: valueKey },
784 + el('label', { className: 'bkbg-spacing-control__label' }, side.label),
785 + el('div', { className: 'bkbg-spacing-control__row' },
786 + el('input', {
787 + type: 'text',
788 + className: 'components-text-control__input',
789 + value: a[valueKey] || '',
790 + placeholder: '0',
791 + onChange: function (e) {
792 + var next = {};
793 + next[valueKey] = e.target.value;
794 + setAttributes(next);
795 + }
796 + }),
797 + el('select', {
798 + className: 'components-select-control__input',
799 + value: a[unitKey] || 'px',
800 + onChange: function (e) {
801 + var next = {};
802 + next[unitKey] = e.target.value;
803 + setAttributes(next);
804 + }
805 + }, spacingUnitOptions.map(function (opt) {
806 + return el('option', { key: opt.value, value: opt.value }, opt.label);
807 + }))
808 + ),
809 + (a[unitKey] || 'px') === 'custom' && el('input', {
810 + type: 'text',
811 + className: 'components-text-control__input bkbg-spacing-control__custom-unit',
812 + value: a[customUnitKey] || '',
813 + placeholder: __('unit, e.g. ch', 'blockenberg'),
814 + onChange: function (e) {
815 + var next = {};
816 + next[customUnitKey] = e.target.value;
817 + setAttributes(next);
818 + }
819 + })
820 + );
821 + })
822 + )
823 + );
824 + }
825 +
706 826 // Get responsive mode icon
707 827 function getModeIcon(mode) {
708 828 switch (mode) {
709 829 case 'tablet': return 'tablet';
@@ -743,46 +863,14 @@
743 863 onClick: equalizeColumns,
744 864 disabled: columnCount < 2
745 865 })
746 866 ),
747 - // Vertical alignment
748 - el(ToolbarGroup, {},
749 - el(ToolbarButton, {
750 - icon: 'arrow-up-alt',
751 - label: __('Align Top', 'blockenberg'),
752 - isPressed: a.vAlign === 'top',
753 - onClick: function () { setAttributes({ vAlign: 'top' }); }
754 - }),
755 - el(ToolbarButton, {
756 - icon: 'minus',
757 - label: __('Align Middle', 'blockenberg'),
758 - isPressed: a.vAlign === 'middle',
759 - onClick: function () { setAttributes({ vAlign: 'middle' }); }
760 - }),
761 - el(ToolbarButton, {
762 - icon: 'arrow-down-alt',
763 - label: __('Align Bottom', 'blockenberg'),
764 - isPressed: a.vAlign === 'bottom',
765 - onClick: function () { setAttributes({ vAlign: 'bottom' }); }
766 - })
767 - )
867 + null
768 868 );
769 869
770 870 // Inspector controls
771 871 var inspector = el(InspectorControls, {},
772 872 el(PanelBody, { title: __('Row Settings', 'blockenberg'), initialOpen: true },
773 - el(SelectControl, {
774 - label: __('Column Gap', 'blockenberg'),
775 - value: a.gap,
776 - options: gapOptions,
777 - onChange: function (v) { setAttributes({ gap: v }); }
778 - }),
779 - el(SelectControl, {
780 - label: __('Vertical Alignment', 'blockenberg'),
781 - value: a.vAlign,
782 - options: vAlignOptions,
783 - onChange: function (v) { setAttributes({ vAlign: v }); }
784 - }),
785 873 el(ToggleControl, {
786 874 label: __('Stack on Mobile', 'blockenberg'),
787 875 help: __('Columns will stack vertically on mobile devices.', 'blockenberg'),
788 876 checked: a.stackOnMobile,
@@ -795,9 +883,9 @@
795 883 onChange: function (v) { setAttributes({ minColumnPct: v }); },
796 884 min: 5,
797 885 max: 25,
798 886 step: 1
799 - })
887 + }),
800 888 ),
801 889 el(PanelBody, { title: __('Column Presets', 'blockenberg'), initialOpen: false },
802 890 el('div', { className: 'bkbg-row-presets' },
803 891 columnPresets.map(function (preset) {
@@ -876,10 +964,10 @@
876 964 orientation: 'horizontal',
877 965 renderAppender: false
878 966 })
879 967 ),
880 - // Render resize handles overlay
881 - columnCount > 1 && el('div', { className: 'bkbg-row__handles-overlay' },
968 + // Render resize handles overlay (hide in mobile mode)
969 + columnCount > 1 && a.responsiveMode !== 'mobile' && el('div', { className: 'bkbg-row__handles-overlay' },
882 970 currentWidths.slice(0, -1).map(function (leftWidth, index) {
883 971 var rightWidth = currentWidths[index + 1];
884 972 var leftOffset = currentWidths.slice(0, index + 1).reduce(function (sum, w) { return sum + w; }, 0);
885 973 return el('div', {
@@ -899,45 +987,75 @@
899 987 })
900 988 );
901 989 })
902 990 ),
903 - // Row selector (left gear)
904 - el('div', { className: 'bkbg-row__row-gear' },
991 + // Floating structure popup trigger
992 + el('div', { className: 'bkbg-row__structure-trigger' },
905 993 el(Button, {
906 - className: 'bkbg-row__row-action-btn bkbg-row__row-action-btn--gear',
907 - icon: 'admin-generic',
908 - label: __('Row settings', 'blockenberg'),
909 - onMouseDown: function(e) {
910 - if (e && e.preventDefault) e.preventDefault();
911 - if (e && e.stopPropagation) e.stopPropagation();
912 - },
913 - onClick: openRowSettings
914 - })
915 - ),
916 - // Row actions on the right side
917 - el('div', { className: 'bkbg-row__row-actions' },
918 - // Insert row above
919 - el(Button, {
920 - className: 'bkbg-row__row-action-btn',
921 - icon: 'plus-alt2',
922 - label: __('Add row above', 'blockenberg'),
923 - onClick: function() { insertRowAt('before'); }
994 + className: 'bkbg-row__structure-btn',
995 + icon: 'layout',
996 + label: __('Row structure', 'blockenberg'),
997 + onClick: function() { setIsStructureOpen(!isStructureOpen); }
924 998 }),
925 - // Add column (center)
926 - el(Button, {
927 - className: 'bkbg-row__row-action-btn bkbg-row__row-action-btn--add-col',
928 - icon: 'plus-alt2',
929 - label: __('Add column', 'blockenberg'),
930 - onClick: addColumn,
931 - disabled: columnCount >= 6
932 - }),
933 - // Insert row below
934 - el(Button, {
935 - className: 'bkbg-row__row-action-btn',
936 - icon: 'plus-alt2',
937 - label: __('Add row below', 'blockenberg'),
938 - onClick: function() { insertRowAt('after'); }
939 - })
999 + isStructureOpen && el(Popover, {
1000 + className: 'bkbg-row__structure-popover',
1001 + position: 'bottom center',
1002 + onClose: function() { setIsStructureOpen(false); },
1003 + focusOnMount: 'container'
1004 + },
1005 + el('div', { className: 'bkbg-row__structure-content' },
1006 + // Header
1007 + el('div', { className: 'bkbg-row__structure-header' },
1008 + el('span', { className: 'bkbg-row__structure-title' }, __('Row Structure', 'blockenberg')),
1009 + el(Button, {
1010 + className: 'bkbg-row__structure-close',
1011 + icon: 'no-alt',
1012 + label: __('Close', 'blockenberg'),
1013 + onClick: function() { setIsStructureOpen(false); }
1014 + })
1015 + ),
1016 + // Visual column bars
1017 + el('div', { className: 'bkbg-row__structure-columns' },
1018 + currentWidths.map(function(width, index) {
1019 + return el('div', {
1020 + key: 'col-' + index,
1021 + className: 'bkbg-row__structure-col',
1022 + style: { width: width + '%' }
1023 + },
1024 + el('span', { className: 'bkbg-row__structure-col-label' }, Math.round(width) + '%')
1025 + );
1026 + })
1027 + ),
1028 + // Column actions
1029 + el('div', { className: 'bkbg-row__structure-actions' },
1030 + el(Button, {
1031 + className: 'bkbg-row__structure-action',
1032 + icon: 'plus',
1033 + onClick: addColumn,
1034 + disabled: columnCount >= 6
1035 + }, __('Add Column', 'blockenberg')),
1036 + el(Button, {
1037 + className: 'bkbg-row__structure-action',
1038 + icon: 'editor-contract',
1039 + onClick: equalizeColumns,
1040 + disabled: columnCount < 2
1041 + }, __('Equalize', 'blockenberg'))
1042 + ),
1043 + // Row actions
1044 + el('div', { className: 'bkbg-row__structure-row-actions' },
1045 + el(Button, {
1046 + className: 'bkbg-row__structure-action bkbg-row__structure-action--secondary',
1047 + icon: 'insert-before',
1048 + onClick: function() { insertRowAt('before'); setIsStructureOpen(false); }
1049 + }, __('Row Above', 'blockenberg')),
1050 + el(Button, {
1051 + className: 'bkbg-row__structure-action bkbg-row__structure-action--secondary',
1052 + icon: 'insert-after',
1053 + onClick: function() { insertRowAt('after'); setIsStructureOpen(false); }
1054 + }, __('Row Below', 'blockenberg'))
1055 + )
1056 + )
1057 + )
940 1058 )
941 1059 );
942 1060 },
943 1061
@@ -946,11 +1064,11 @@
946 1064 var blockProps = useBlockProps.save({
947 1065 className: [
948 1066 'bkbg-row',
949 1067 'bkbg-row--gap-' + a.gap,
950 - 'bkbg-row--valign-' + a.vAlign,
951 1068 a.stackOnMobile ? 'bkbg-row--stack-mobile' : ''
952 1069 ].filter(Boolean).join(' '),
1070 + style: getSpacingStyles(a),
953 1071 'data-stack-mobile': a.stackOnMobile ? '1' : '0'
954 1072 });
955 1073
956 1074 return el('div', blockProps,
@@ -959,5 +1077,5 @@
959 1077 )
960 1078 );
961 1079 }
962 1080 });
963 -});
1081 +}() );