Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nav-menu-item container monitor for dynamic container creation #838

Open
wants to merge 1 commit into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/metaboxes/lib/constants.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const PAGE_NOW_WIDGETS = 'widgets.php';
export const PAGE_NOW_CUSTOMIZE = 'customize.php';
export const PAGE_NOW_NAV = 'nav-menus.php';

export const CARBON_FIELDS_CONTAINER_ID_PREFIX = 'carbon_fields_container_';
export const CARBON_FIELDS_CONTAINER_WIDGET_ID_PREFIX = 'carbon_fields_';
7 changes: 6 additions & 1 deletion packages/metaboxes/monitors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import { Fragment, render } from '@wordpress/element';
import SaveLock from './save-lock';
import ConditionalDisplay from './conditional-display';
import WidgetHandler from './widget-handler';
import NavMenuHandler from './nav-menu-handler';
import RevisionsFlag from './revisions-flag';
import isGutenberg from '../utils/is-gutenberg';
import { PAGE_NOW_WIDGETS, PAGE_NOW_CUSTOMIZE } from '../lib/constants';
import { PAGE_NOW_WIDGETS, PAGE_NOW_CUSTOMIZE, PAGE_NOW_NAV } from '../lib/constants';

/**
* Initializes the monitors.
Expand All @@ -32,6 +33,10 @@ export default function initializeMonitors( context ) {
<WidgetHandler />
) }

{ ( pagenow === PAGE_NOW_NAV ) && (
<NavMenuHandler />
) }

<ConditionalDisplay context={ context } />
</Fragment>,
document.createElement( 'div' )
Expand Down
147 changes: 147 additions & 0 deletions packages/metaboxes/monitors/nav-menu-handler/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/**
* External dependencies.
*/
import { dispatch, select } from '@wordpress/data';
import { unmountComponentAtNode } from '@wordpress/element';
import { flow } from 'lodash';
import { withEffects } from 'refract-callbag';
import {
map,
merge,
pipe,
filter
} from 'callbag-basics';

/**
* Internal dependencies.
*/
import urldecode from '../../utils/urldecode';
import flattenField from '../../utils/flatten-field';
import fromEventPattern from '../../utils/from-event-pattern';
import { renderContainer } from '../../containers';
import { CARBON_FIELDS_CONTAINER_ID_PREFIX } from '../../lib/constants';

/**
* Performs the re-initialization of nav menu items.
*
* @return {null}
*/
function NavMenuHandler() {
return null;
}

function hasCarbonFields( $node ) {
return $node.find( `[data-id^="${ CARBON_FIELDS_CONTAINER_ID_PREFIX }"]` ).length > 0;
}

/**
* The function that controls the stream of side effects.
*
* @return {Object}
*/
function aperture() {
return merge(
pipe(
fromEventPattern(
( handler ) => window.jQuery( document ).on( 'menu-item-added', handler ),
( handler ) => window.jQuery( document ).off( 'menu-item-added', handler ),
( event, $nodes ) => ( {
event,
$nodes
} )
),
filter( ( { $nodes } ) => {
return hasCarbonFields( $nodes );
} ),
map( ( payload ) => ( {
type: 'NAV_MENU_ITEMS_ADDED',
payload
} ) )
),

pipe(
fromEventPattern(
( handler ) => window.jQuery( document ).on( 'menu-removing-item', handler ),
( handler ) => window.jQuery( document ).off( 'menu-removing-item', handler ),
( event, $node ) => ( {
event,
$node
} )
),
filter( ( { $node } ) => {
return hasCarbonFields( $node );
} ),
map( ( payload ) => ( {
type: 'NAV_MENU_ITEM_DELETED',
payload
} ) )
)
);
}

/**
* The function that causes the side effects.
*
* @return {Function}
*/
function handler() {
return function( effect ) {
const { getContainerById } = select( 'carbon-fields/metaboxes' );
const {
addContainer,
removeContainer,
addFields,
removeFields
} = dispatch( 'carbon-fields/metaboxes' );

switch ( effect.type ) {
case 'NAV_MENU_ITEMS_ADDED': {
const { $nodes } = effect.payload;

const containers = $nodes.map( ( index, node ) => {
const $node = window.jQuery( node );
if ( $node.find( '[data-json]' ).length > 0 ) {
return flow(
urldecode,
JSON.parse
)( $node.find( '[data-json]' ).data( 'json' ) );
}
} );

containers.each( ( index, container ) => {
const fields = [];
container.fields = container.fields.map( ( field ) => flattenField( field, container, fields ) );

addFields( fields );
addContainer( container );

renderContainer( container, 'classic' );
} );

break;
}

case 'NAV_MENU_ITEM_DELETED': {
const { $node } = effect.payload;
const containerId = $node.find( `[data-id^="${ CARBON_FIELDS_CONTAINER_ID_PREFIX }"]` ).data( 'id' );

// Get the container from the store.
const container = getContainerById( containerId );

// Remove the current instance from DOM.
unmountComponentAtNode( document.querySelector( `.container-${ containerId }` ) );

// Get the fields that belongs to the container.
const fieldsIds = _.map( container.fields, 'id' );

// Remove everything from the store.
removeContainer( containerId );
removeFields( fieldsIds );

break;
}
}
};
}

export default withEffects( aperture, { handler } )( NavMenuHandler );