我正在使用以下电子商务框架:
商店演示:https://cornerstone-light-demo.mybigcommerce.com/
Git:https://github.com/bigcommerce/cornerstone
我正在尝试将导航转换为纯粹基于点击的形式,就像在上面的商店演示中一样,使用相同框架的商店中看到的点击/悬停组合:rockbottomgolf.com
我已经隔离了以下js文件中所需的更改:
import $ from 'jquery';
import _ from 'lodash';
import mediaQueryListFactory from './media-query-list';
const PLUGIN_KEY = 'collapsible';
export const CollapsibleEvents = {
open: 'open.collapsible',
close: 'close.collapsible',
toggle: 'toggle.collapsible',
click: 'click.collapsible',
};
const CollapsibleState = {
closed: 'closed',
open: 'open',
};
function prependHash(id) {
if (id && id.indexOf('#') === 0) {
return id;
}
return `#${id}`;
}
function optionsFromData($element) {
return {
disabledBreakpoint: $element.data(`${PLUGIN_KEY}-disabled-breakpoint`),
disabledState: $element.data(`${PLUGIN_KEY}-disabled-state`),
enabledState: $element.data(`${PLUGIN_KEY}-enabled-state`),
openClassName: $element.data(`${PLUGIN_KEY}-open-class-name`),
};
}
/**
* Collapse/Expand toggle
*/
export class Collapsible {
/**
* @param {jQuery} $toggle - Trigger button
* @param {jQuery} $target - Content to collapse / expand
* @param {Object} [options] - Configurable options
* @param {Object} [options.$context]
* @param {Object} [options.disabledBreakpoint]
* @param {Object} [options.disabledState]
* @param {Object} [options.enabledState]
* @param {Object} [options.openClassName]
* @example
*
* <button id="#more">Collapse</button>
* <div id="content">...</div>
*
* new Collapsible($('#more'), $('#content'));
*/
constructor($toggle, $target, {
disabledBreakpoint,
disabledState,
enabledState,
openClassName = 'is-open',
} = {}) {
this.$toggle = $toggle;
this.$target = $target;
this.targetId = $target.attr('id');
this.openClassName = openClassName;
this.disabledState = disabledState;
this.enabledState = enabledState;
if (disabledBreakpoint) {
this.disabledMediaQueryList = mediaQueryListFactory(disabledBreakpoint);
}
if (this.disabledMediaQueryList) {
this.disabled = this.disabledMediaQueryList.matches;
} else {
this.disabled = false;
}
// Auto-bind
this.onClicked = this.onClicked.bind(this);
this.onDisabledMediaQueryListMatch = this.onDisabledMediaQueryListMatch.bind(this);
// Assign DOM attributes
this.$target.attr('aria-hidden', this.isCollapsed);
this.$toggle
.attr('aria-controls', $target.attr('id'))
.attr('aria-expanded', this.isOpen);
// Listen
this.bindEvents();
}
get isCollapsed() {
return !this.$target.hasClass(this.openClassName) || this.$target.is(':hidden');
}
get isOpen() {
return !this.isCollapsed;
}
set disabled(disabled) {
this._disabled = disabled;
if (disabled) {
this.toggleByState(this.disabledState);
} else {
this.toggleByState(this.enabledState);
}
}
get disabled() {
return this._disabled;
}
open({ notify = true } = {}) {
this.$toggle
.addClass(this.openClassName)
.attr('aria-expanded', true);
this.$target
.addClass(this.openClassName)
.attr('aria-hidden', false);
if (notify) {
this.$toggle.trigger(CollapsibleEvents.open, [this]);
this.$toggle.trigger(CollapsibleEvents.toggle, [this]);
}
}
close({ notify = true } = {}) {
this.$toggle
.removeClass(this.openClassName)
.attr('aria-expanded', false);
this.$target
.removeClass(this.openClassName)
.attr('aria-hidden', true);
if (notify) {
this.$toggle.trigger(CollapsibleEvents.close, [this]);
this.$toggle.trigger(CollapsibleEvents.toggle, [this]);
}
}
toggle() {
if (this.isCollapsed) {
this.open();
} else {
this.close();
}
}
toggleByState(state, ...args) {
switch (state) {
case CollapsibleState.open:
return this.open.apply(this, args);
case CollapsibleState.closed:
return this.close.apply(this, args);
default:
return undefined;
}
}
hasCollapsible(collapsibleInstance) {
return $.contains(this.$target.get(0), collapsibleInstance.$target.get(0));
}
bindEvents() {
this.$toggle.on(CollapsibleEvents.click, this.onClicked);
if (this.disabledMediaQueryList && this.disabledMediaQueryList.addListener) {
this.disabledMediaQueryList.addListener(this.onDisabledMediaQueryListMatch);
}
}
unbindEvents() {
this.$toggle.off(CollapsibleEvents.click, this.onClicked);
if (this.disabledMediaQueryList && this.disabledMediaQueryList.removeListener) {
this.disabledMediaQueryList.removeListener(this.onDisabledMediaQueryListMatch);
}
}
onClicked(event) {
if (this.disabled) {
return;
}
event.preventDefault();
this.toggle();
}
onDisabledMediaQueryListMatch(media) {
this.disabled = media.matches;
}
}
/**
* Convenience method for constructing Collapsible instance
*
* @param {string} [selector]
* @param {Object} [options]
* @param {Object} [options.$context]
* @param {Object} [options.disabledBreakpoint]
* @param {Object} [options.disabledState]
* @param {Object} [options.enabledState]
* @param {Object} [options.openClassName]
* @return {Array} array of Collapsible instances
*
* @example
* <a href="#content" data-collapsible>Collapse</a>
* <div id="content">...</div>
*
* collapsibleFactory();
*/
export default function collapsibleFactory(selector = `[data-${PLUGIN_KEY}]`, overrideOptions = {}) {
const $collapsibles = $(selector, overrideOptions.$context);
return $collapsibles.map((index, element) => {
const $toggle = $(element);
const instanceKey = `${PLUGIN_KEY}-instance`;
const cachedCollapsible = $toggle.data(instanceKey);
if (cachedCollapsible instanceof Collapsible) {
return cachedCollapsible;
}
const targetId = prependHash($toggle.data(PLUGIN_KEY) ||
$toggle.data(`${PLUGIN_KEY}-target`) ||
$toggle.attr('href'));
const options = _.extend(optionsFromData($toggle), overrideOptions);
const collapsible = new Collapsible($toggle, $(targetId), options);
$toggle.data(instanceKey, collapsible);
return collapsible;
}).toArray();
}
和
import $ from 'jquery';
import collapsibleFactory from '../common/collapsible';
import collapsibleGroupFactory from '../common/collapsible-group';
const PLUGIN_KEY = 'menu';
/*
* Manage the behaviour of a menu
* @param {jQuery} $menu
*/
class Menu {
constructor($menu) {
this.$menu = $menu;
this.$body = $('body');
this.hasMaxMenuDisplayDepth = this.$body.find('.navPages-list').hasClass('navPages-list-depth-max');
// Init collapsible
this.collapsibles = collapsibleFactory('[data-collapsible]', { $context: this.$menu });
this.collapsibleGroups = collapsibleGroupFactory($menu);
// Auto-bind
this.onMenuClick = this.onMenuClick.bind(this);
this.onDocumentClick = this.onDocumentClick.bind(this);
// Listen
this.bindEvents();
}
collapseAll() {
this.collapsibles.forEach(collapsible => collapsible.close());
this.collapsibleGroups.forEach(group => group.close());
}
collapseNeighbors($neighbors) {
const $collapsibles = collapsibleFactory('[data-collapsible]', { $context: $neighbors });
$collapsibles.forEach($collapsible => $collapsible.close());
}
bindEvents() {
this.$menu.on('click', this.onMenuClick);
this.$body.on('click', this.onDocumentClick);
}
unbindEvents() {
this.$menu.off('click', this.onMenuClick);
this.$body.off('click', this.onDocumentClick);
}
onMenuClick(event) {
event.stopPropagation();
if (this.hasMaxMenuDisplayDepth) {
const $neighbors = $(event.target).parent().siblings();
this.collapseNeighbors($neighbors);
}
}
onDocumentClick() {
this.collapseAll();
}
}
/*
* Create a new Menu instance
* @param {string} [selector]
* @return {Menu}
*/
export default function menuFactory(selector = `[data-${PLUGIN_KEY}]`) {
const $menu = $(selector).eq(0);
const instanceKey = `${PLUGIN_KEY}-instance`;
const cachedMenu = $menu.data(instanceKey);
if (cachedMenu instanceof Menu) {
return cachedMenu;
}
const menu = new Menu($menu);
$menu.data(instanceKey, menu);
return menu;
}
但我似乎无法弄清楚它们是如何相互协作的,特别是绑定/取消绑定,这似乎是我需要编辑的内容。