169 lines
5.6 KiB
JavaScript
169 lines
5.6 KiB
JavaScript
// External dependencies
|
|
import debounce from 'lodash/debounce';
|
|
import endsWith from 'lodash/endsWith';
|
|
import filter from 'lodash/filter';
|
|
import forEach from 'lodash/forEach';
|
|
import get from 'lodash/get';
|
|
import groupBy from 'lodash/groupBy';
|
|
import isArray from 'lodash/isArray';
|
|
import isEmpty from 'lodash/isEmpty';
|
|
import isObject from 'lodash/isObject';
|
|
import map from 'lodash/map';
|
|
import size from 'lodash/size';
|
|
|
|
// Internal dependencies
|
|
import * as DOM from '@frontend-builder/features/scroll-effects/fe/io/dom';
|
|
import { ETBuilderScrollEffects } from '@frontend-builder/features/scroll-effects';
|
|
import { convertTransforms } from '@frontend-builder/features/scroll-effects/vb/helpers';
|
|
import { fromObject } from '@frontend-builder/features/scroll-effects/entities/scroll-effect';
|
|
import ETBuilderOffsetsConst from '@frontend-builder/constants/et-builder-offsets-const';
|
|
import { Window } from '@frontend-builder/features/scroll-effects/stores/window';
|
|
import { top_window } from '@core/admin/js/frame-helpers';
|
|
import { getContentAreaSelector } from 'gutenberg/utils/selectors';
|
|
|
|
|
|
const isBlockEditor = $(top_window.document).find(getContentAreaSelector(top_window, true)).length > 0;
|
|
const isBuilder = isObject(window.ET_Builder) && size(window.ET_Builder) > 1 && ! isBlockEditor;
|
|
|
|
const getViewportWidth = () => jQuery(window).width();
|
|
const toItem = (effects, id) => ({ id, effects: (effects || []).map(fromObject) });
|
|
const getSelector = (step, id) => `body[data-scroll-step="${step}"] ${id}`;
|
|
const scrollEffects = new ETBuilderScrollEffects(DOM, getSelector);
|
|
|
|
let motionEffectsLoaded = false;
|
|
|
|
const getMotionElementsFE = () => {
|
|
const allMotionElements = window.et_pb_motion_elements || [];
|
|
|
|
// We are on the real FE, so load all the motion elements.
|
|
if (! isBuilder || isEmpty(allMotionElements)) {
|
|
return allMotionElements;
|
|
}
|
|
|
|
/**
|
|
* We're inside the Builder, but still need to render motion effects from the TB
|
|
* Get all the motion elements for TB parts of the layout.
|
|
*/
|
|
const breakpoints = ['desktop', 'tablet', 'phone'];
|
|
const tb_motion_elements = {};
|
|
|
|
forEach(breakpoints, breakpoint => {
|
|
tb_motion_elements[breakpoint] = filter(window.et_pb_motion_elements[breakpoint], el => (endsWith(el.id, '_tb_header') || endsWith(el.id, '_tb_body') || endsWith(el.id, '_tb_footer')));
|
|
});
|
|
|
|
return tb_motion_elements;
|
|
};
|
|
|
|
const toggleMotionElements = (state = 'hide') => {
|
|
const activeMotionElements = getMotionElementsFE();
|
|
const elementsList = get(activeMotionElements, 'desktop', []);
|
|
|
|
if (isEmpty(elementsList)) {
|
|
return;
|
|
}
|
|
|
|
forEach(elementsList, element => {
|
|
const $element = jQuery(element.id);
|
|
if ('hide' === state) {
|
|
$element.addClass('et-pb-before-scroll-animation');
|
|
} else {
|
|
$element.removeClass('et-pb-before-scroll-animation et_animated et-waypoint');
|
|
$element.addClass('et_had_animation');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @param {number} width
|
|
* @returns {string}
|
|
*/
|
|
const getDevice = width => {
|
|
if (width <= ETBuilderOffsetsConst.responsiveLandscape.phone) {
|
|
return 'phone';
|
|
}
|
|
|
|
if (width <= ETBuilderOffsetsConst.responsiveLandscape.tablet) {
|
|
return 'tablet';
|
|
}
|
|
|
|
return 'desktop';
|
|
};
|
|
|
|
const getChildren = (element, effects) => {
|
|
const childrenCount = parseInt(element.children_count);
|
|
const children = {};
|
|
|
|
if (childrenCount > 0) {
|
|
for (let i = 0; i < childrenCount; i++) {
|
|
const key = `.${element.module_type}_item_${element.module_index}_${i}`;
|
|
children[key] = effects;
|
|
}
|
|
}
|
|
|
|
return children;
|
|
};
|
|
|
|
/**
|
|
* @param {[object]} elements
|
|
* @param {string} device
|
|
*/
|
|
const updateItems = (elements, device) => {
|
|
const groupedElements = groupBy(elements[device] || [], 'id');
|
|
map(groupedElements, toItem)
|
|
.forEach(({ id, effects }) => {
|
|
if (! isEmpty(effects) && isArray(effects)) {
|
|
const element = get(groupedElements, [id, '0'], {});
|
|
const start = get(element, 'trigger_start', 'middle');
|
|
const end = start; // Use only one offset point right now. Update the value here if/when we add second point.
|
|
const motionTriggers = { start, end };
|
|
|
|
if ('on' === element.grid_motion) {
|
|
if (! element.child_slug) {
|
|
const children = getChildren(element, effects);
|
|
|
|
forEach(children, (childModule, id) => {
|
|
scrollEffects.add(id, childModule, motionTriggers);
|
|
});
|
|
}
|
|
} else {
|
|
const transforms = isEmpty(element.transforms) ? [] : convertTransforms(element.transforms);
|
|
scrollEffects.add(id, [...transforms, ...effects], motionTriggers);
|
|
}
|
|
} else {
|
|
scrollEffects.remove(id);
|
|
}
|
|
});
|
|
|
|
if (! motionEffectsLoaded) {
|
|
motionEffectsLoaded = true;
|
|
setTimeout(() => toggleMotionElements('show'), 200);
|
|
}
|
|
};
|
|
|
|
const activeMotionElements = getMotionElementsFE();
|
|
|
|
if (! isEmpty(activeMotionElements)) {
|
|
const $window = $(window);
|
|
const windowStore = new Window($window.width(), $window.height());
|
|
const elements = activeMotionElements;
|
|
let device = getDevice(getViewportWidth());
|
|
|
|
// Hide all the elements on page load so scroll effects applied smoothly
|
|
toggleMotionElements();
|
|
|
|
jQuery(window).on('load', debounce(() => updateItems(elements, device), 500));
|
|
jQuery(window).on('resize', debounce(() => windowStore.setWidth($window.width()).setHeight($window.height()), 500));
|
|
|
|
windowStore.addSizeChangeListener(() => {
|
|
const newDevice = getDevice(getViewportWidth());
|
|
|
|
if (newDevice !== device) {
|
|
device = newDevice;
|
|
|
|
updateItems(elements, device);
|
|
} else {
|
|
scrollEffects.refresh();
|
|
}
|
|
});
|
|
}
|