Skip to content

Commit

Permalink
PLANET-7382 Move Submenu block into master theme (#2195)
Browse files Browse the repository at this point in the history
- Moved the Submenu block to master theme with associated funcctions
  • Loading branch information
Osong-Michael authored Jan 17, 2024
1 parent f8700b6 commit 0885a14
Show file tree
Hide file tree
Showing 21 changed files with 944 additions and 1 deletion.
71 changes: 71 additions & 0 deletions assets/src/blocks/Submenu/SubmenuBlock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {SubmenuEditor} from './SubmenuEditor.js';
import {example} from './example';
import {getStyleLabel} from '../../functions/getStyleLabel';

const {__} = wp.i18n;

const BLOCK_NAME = 'planet4-blocks/submenu';

export const registerSubmenuBlock = () => {
const {registerBlockType} = wp.blocks;

registerBlockType(BLOCK_NAME, {
title: 'Submenu',
icon: 'welcome-widgets-menus',
category: 'planet4-blocks',
attributes: {
title: {
type: 'string',
default: '',
},
submenu_style: { // Needed for old blocks conversion
type: 'integer',
default: 0,
},
levels: {
type: 'array',
default: [{heading: 2, link: false, style: 'none'}],
},
isExample: {
type: 'boolean',
default: false,
},
exampleMenuItems: { // Used for the block's preview, which can't extract items from anything.
type: 'array',
},
},
supports: {
multiple: false, // Use the block just once per post.
html: false,
},
styles: [
{
name: 'long',
label: getStyleLabel(
__('Long full-width', 'planet4-blocks-backend'),
__('Use: on long pages (more than 5 screens) when list items are long (+ 10 words). No max items recommended.', 'planet4-blocks-backend')
),
isDefault: true,
},
{
name: 'short',
label: getStyleLabel(
__('Short full-width', 'planet4-blocks-backend'),
__('Use: on long pages (more than 5 screens) when list items are short (up to 5 words). No max items recommended.', 'planet4-blocks-backend')
),
},
{
name: 'sidebar',
label: getStyleLabel(
__('Short sidebar', 'planet4-blocks-backend'),
__('Use: on long pages (more than 5 screens) when list items are short (up to 10 words). Max items recommended: 9', 'planet4-blocks-backend')
),
},
],
edit: SubmenuEditor,
save() {
return null;
},
example,
});
};
126 changes: 126 additions & 0 deletions assets/src/blocks/Submenu/SubmenuEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import {Button, PanelBody} from '@wordpress/components';
import {SubmenuLevel} from './SubmenuLevel';
import {SubmenuItems} from './SubmenuItems';
import {InspectorControls, RichText} from '@wordpress/block-editor';
import {getSubmenuStyle} from './getSubmenuStyle';
import {makeHierarchical} from './makeHierarchical';
import {getHeadingsFromBlocks} from './getHeadingsFromBlocks';
import {useSelect} from '@wordpress/data';
import {deepClone} from '../../functions/deepClone';

const {__} = wp.i18n;

const renderEdit = (attributes, setAttributes) => {
function addLevel() {
const [previousLastLevel] = attributes.levels.slice(-1);
const newLevel = previousLastLevel.heading + 1;
setAttributes({levels: attributes.levels.concat({heading: newLevel, link: false, style: 'none'})});
}

function onHeadingChange(index, value) {
const levels = deepClone(attributes.levels);
levels[index].heading = Number(value);
setAttributes({levels});
}

function onLinkChange(index, value) {
const levels = deepClone(attributes.levels);
levels[index].link = value;
setAttributes({levels});
}

function onStyleChange(index, value) {
const levels = deepClone(attributes.levels);
levels[index].style = value; // Possible values: "none", "bullet", "number"
setAttributes({levels});
}

function removeLevel() {
setAttributes({levels: attributes.levels.slice(0, -1)});
}

function getMinLevel(attr, index) {
if (index === 0) {
return null;
}

return attr.levels[index - 1].heading;
}

return (
<InspectorControls>
<PanelBody title={__('Settings', 'planet4-blocks-backend')}>
{attributes.levels.map((level, i) => (
<SubmenuLevel
{...level}
onHeadingChange={onHeadingChange}
onLinkChange={onLinkChange}
onStyleChange={onStyleChange}
index={i}
key={i}
minLevel={getMinLevel(attributes, i)}
/>
))}
<Button
isPrimary
onClick={addLevel}
disabled={attributes.levels.length >= 3 || attributes.levels.slice(-1)[0].heading === 0}
style={{marginRight: 5}}
>
{__('Add level', 'planet4-blocks-backend')}
</Button>
<Button
variant="secondary"
onClick={removeLevel}
disabled={attributes.levels.length <= 1}
>
{__('Remove level', 'planet4-blocks-backend')}
</Button>
</PanelBody>
</InspectorControls>
);
};

const renderView = (attributes, setAttributes, className) => {
const {
title,
levels,
submenu_style,
isExample,
exampleMenuItems,
} = attributes;

const blocks = useSelect(select => select('core/block-editor').getBlocks(), null);

const flatHeadings = getHeadingsFromBlocks(blocks, levels);

const menuItems = isExample ? exampleMenuItems : makeHierarchical(flatHeadings);

const style = getSubmenuStyle(className, submenu_style);

return (
<section className={`block submenu-block submenu-${style} ${className ?? ''}`}>
<RichText
tagName="h2"
placeholder={__('Enter title', 'planet4-blocks-backend')}
value={title}
onChange={titl => setAttributes({title: titl})}
withoutInteractiveFormatting
allowedFormats={[]}
/>
{menuItems.length > 0 ?
<SubmenuItems menuItems={menuItems} /> :
<div className="EmptyMessage">
{__('The submenu block produces no output on the editor.', 'planet4-blocks-backend')}
</div>
}
</section>
);
};

export const SubmenuEditor = ({attributes, setAttributes, isSelected, className}) => (
<>
{isSelected && renderEdit(attributes, setAttributes)}
{renderView(attributes, setAttributes, className)}
</>
);
19 changes: 19 additions & 0 deletions assets/src/blocks/Submenu/SubmenuFrontend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {getSubmenuStyle} from './getSubmenuStyle';
import {SubmenuItems} from './SubmenuItems';
import {makeHierarchical} from './makeHierarchical';
import {getHeadingsFromDom} from './getHeadingsFromDom';

export const SubmenuFrontend = ({title, className, levels, submenu_style}) => {
const headings = getHeadingsFromDom(levels);
const menuItems = makeHierarchical(headings);
const style = getSubmenuStyle(className, submenu_style);

return (
<section className={`block submenu-block submenu-${style} ${className ?? ''}`}>
{!!title && (
<h2>{ title }</h2>
)}
<SubmenuItems menuItems={menuItems} />
</section>
);
};
30 changes: 30 additions & 0 deletions assets/src/blocks/Submenu/SubmenuItems.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export const SubmenuItems = ({menuItems}) => {
const renderMenuItems = items => {
return items.map(({anchor, text, style, shouldLink, children}) => (
<li key={anchor} className={`list-style-${style || 'none'} ${shouldLink ? 'list-link' : 'list-heading'}`}>
{shouldLink ?
<a
className="icon-link submenu-link"
href={`#${anchor}`}
>
{text}
</a> :
<span className="submenu-heading">{text}</span>
}
{children && children.length > 0 &&
<ul>
{renderMenuItems(children)}
</ul>
}
</li>
));
};

return menuItems.length > 0 && (
<div className="submenu-menu">
<ul className="submenu-item">
{renderMenuItems(menuItems)}
</ul>
</div>
);
};
61 changes: 61 additions & 0 deletions assets/src/blocks/Submenu/SubmenuLevel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
CheckboxControl,
SelectControl,
} from '@wordpress/components';

const {__} = wp.i18n;

const getHeadingOptions = minLevel => {
return [
{label: __('Heading 2', 'planet4-blocks-backend'), value: 2},
{label: __('Heading 3', 'planet4-blocks-backend'), value: 3},
{label: __('Heading 4', 'planet4-blocks-backend'), value: 4},
{label: __('Heading 5', 'planet4-blocks-backend'), value: 5},
{label: __('Heading 6', 'planet4-blocks-backend'), value: 6},
].map(option => ({...option, disabled: option.value <= minLevel}));
};

export const SubmenuLevel = props => {
const {
index,
heading,
onLinkChange,
link,
onHeadingChange,
style,
onStyleChange,
minLevel,
} = props;

return (
<div>
<p>{`${__('Level', 'planet4-blocks-backend')} ${Number(index + 1)}`}</p>
<SelectControl
label={__('Submenu item', 'planet4-blocks-backend')}
value={heading}
options={getHeadingOptions(minLevel)}
onChange={e => onHeadingChange(index, e)}
/>

<CheckboxControl
label={__('Link', 'planet4-blocks-backend')}
value={link}
checked={link}
onChange={e => onLinkChange(index, e)}
className="submenu-level-link"
/>

<SelectControl
label={__('List style', 'planet4-blocks-backend')}
value={style}
options={[
{label: __('None', 'planet4-blocks-backend'), value: 'none'},
{label: __('Bullet', 'planet4-blocks-backend'), value: 'bullet'},
{label: __('Number', 'planet4-blocks-backend'), value: 'number'},
]}
onChange={e => onStyleChange(index, e)}
/>
<hr />
</div>
);
};
Loading

0 comments on commit 0885a14

Please sign in to comment.