/* eslint-disable max-depth */ /* eslint-disable max-len */ import BaseFoundation, { DefaultAdapter } from '../base/foundation'; import NavItem from './NavItem'; import { ItemProps } from './itemFoundation'; import { strings } from './constants'; import { get } from 'lodash-es'; import isNullOrUndefined from '../utils/isNullOrUndefined'; export interface ItemKey2ParentKeysMap { [key: string]: (string | number)[]; } export interface OnClickData { itemKey: string | number; domEvent: any; isOpen: boolean; } export interface OnSelectData extends OnClickData { selectedKeys: (string | number)[]; selectedItems: ItemProps[]; } export interface OnOpenChangeData extends OnClickData { openKeys: (string | number)[]; } export interface NavItemType { props?: ItemProps; items?: NavItemType[]; [key: string]: any; } export interface NavigationAdapter

, S = Record> extends DefaultAdapter { notifySelect(data: OnSelectData): void; notifyOpenChange(data: OnOpenChangeData): void; setIsCollapsed(isCollapsed: boolean): void; notifyCollapseChange(isCollapsed: boolean): void; updateItems(items: ItemProps[]): void; setItemKeysMap(map: { [key: string]: (string | number)[] }): void; addSelectedKeys(...keys: (string | number)[]): void; removeSelectedKeys(...keys: (string | number)[]): void; updateSelectedKeys(keys: (string | number)[]): void; updateOpenKeys(keys: (string | number)[]): void; addOpenKeys(...keys: (string | number)[]): void; removeOpenKeys(...keys: (string | number)[]): void; setItemsChanged(isChanged: boolean): void; } export default class NavigationFoundation

, S = Record> extends BaseFoundation, P, S> { constructor(adapter: NavigationAdapter) { super({ ...adapter }); } static getZeroParentKeys(itemKeysMap = {}, ...itemKeys: (string | number)[]) { const willAddKeys = []; if (itemKeys.length) { for (const itemKey of itemKeys) { if (Array.isArray(itemKeysMap[itemKey]) && itemKeysMap[itemKey].length) { const levelZeroParentKey = itemKeysMap[itemKey][0]; if (!isNullOrUndefined(levelZeroParentKey)) { willAddKeys.push(levelZeroParentKey); } } } } return willAddKeys; } static buildItemKeysMap(items: NavItemType[] = [], keysMap = {}, parentKeys: (string | number)[] = [], keyPropName = 'itemKey') { if (Array.isArray(items) && items.length) { for (const item of items) { if (Array.isArray(item)) { NavigationFoundation.buildItemKeysMap(item, keysMap, [...parentKeys], keyPropName); } else { let itemKey; if (item && typeof item === 'object') { itemKey = item[keyPropName] || (item.props && item.props[keyPropName]); } if (itemKey) { keysMap[itemKey] = [...parentKeys]; if (Array.isArray(item.items) && item.items.length) { NavigationFoundation.buildItemKeysMap( item.items, keysMap, [...parentKeys, itemKey], keyPropName ); } else if (item.props && item.props.children) { const children = Array.isArray(item.props.children) ? item.props.children : [item.props.children]; NavigationFoundation.buildItemKeysMap( children, keysMap, [...parentKeys, itemKey], keyPropName ); } } } } } return keysMap; } /** * init is called in constructor and componentDidMount. * if you want to update state in constructor, please add it to return object; * if you want to update state in componentDidMount, please call adapter in else logic. * @param {*} lifecycle * @returns */ init(lifecycle: string) { const { defaultSelectedKeys, selectedKeys } = this.getProps(); let willSelectedKeys = selectedKeys || defaultSelectedKeys || []; const { itemKeysMap, willOpenKeys, formatedItems } = this.getCalcState(); const parentSelectKeys = this.selectLevelZeroParentKeys(itemKeysMap, willSelectedKeys); willSelectedKeys = willSelectedKeys.concat(parentSelectKeys); if (lifecycle === 'constructor') { return { selectedKeys: willSelectedKeys, itemKeysMap, openKeys: willOpenKeys, items: formatedItems, }; } else { this._adapter.updateSelectedKeys(willSelectedKeys); this._adapter.setItemKeysMap(itemKeysMap); this._adapter.updateOpenKeys(willOpenKeys); this._adapter.updateItems(formatedItems); this._adapter.setItemsChanged(true); } return undefined; } /** * Get the state to be calculated */ getCalcState() { const { itemKeysMap, formatedItems } = this.getFormatedItems(); const willOpenKeys = this.getWillOpenKeys(itemKeysMap); return { itemKeysMap, willOpenKeys, formatedItems }; } /** * Calculate formatted items and itemsKeyMap */ getFormatedItems() { const { items, children } = this.getProps(); const formatedItems = this.formatItems(items); const willHandleItems = Array.isArray(items) && items.length ? formatedItems : children; const itemKeysMap = NavigationFoundation.buildItemKeysMap(willHandleItems); return { itemKeysMap, formatedItems }; } /** * Calculate the keys that will need to be opened soon * @param {*} itemKeysMap */ getWillOpenKeys(itemKeysMap: ItemKey2ParentKeysMap) { const { defaultOpenKeys, openKeys, defaultSelectedKeys, selectedKeys, mode } = this.getProps(); let willOpenKeys = openKeys || defaultOpenKeys || []; if ( !(Array.isArray(defaultOpenKeys) || Array.isArray(openKeys)) && mode === strings.MODE_VERTICAL && (Array.isArray(defaultSelectedKeys) || Array.isArray(selectedKeys)) ) { const currentSelectedKeys = Array.isArray(selectedKeys) ? selectedKeys : defaultSelectedKeys; willOpenKeys = this.getShouldOpenKeys(itemKeysMap, currentSelectedKeys); } return [...willOpenKeys]; } getItemKey(item: string | number, keyPropName = 'itemKey') { if (item && typeof item === 'object') { return item[keyPropName]; } return item; } getShouldOpenKeys(itemKeysMap: ItemKey2ParentKeysMap = {}, selectedKeys: string | number[] = []) { const willOpenKeySet = new Set(); if (Array.isArray(selectedKeys) && selectedKeys.length) { selectedKeys.forEach(item => { if (item) { const parentKeys = get(itemKeysMap, this.getItemKey(item)); if (Array.isArray(parentKeys)) { parentKeys.forEach(k => willOpenKeySet.add(k)); } } }); } return [...willOpenKeySet]; } destroy() {} // eslint-disable-line selectLevelZeroParentKeys(itemKeysMap: ItemKey2ParentKeysMap, ...itemKeys: (string | number)[]) { const _itemKeysMap = isNullOrUndefined(itemKeysMap) ? this.getState('itemKeysMap') : itemKeysMap; // console.log(itemKeysMap); const willAddKeys = []; if (itemKeys.length) { for (const itemKey of itemKeys) { if (Array.isArray(_itemKeysMap[itemKey]) && _itemKeysMap[itemKey].length) { const levelZeroParentKey = _itemKeysMap[itemKey][0]; if (!isNullOrUndefined(levelZeroParentKey)) { willAddKeys.push(levelZeroParentKey); } } } } if (willAddKeys.length) { return willAddKeys; } return []; } formatItems(items: ItemProps[] = []) { const formatedItems = []; for (const item of items) { formatedItems.push(new NavItem(item)); } return formatedItems; } handleSelect(data: OnSelectData) { this._adapter.notifySelect(data); } judgeIfOpen(openKeys: (string | number)[], items: NavItemType[]): boolean { let shouldBeOpen = false; const _openKeys = Array.isArray(openKeys) ? openKeys : openKeys && [openKeys]; if (_openKeys && Array.isArray(items) && items.length) { for (const item of items) { shouldBeOpen = _openKeys.includes(item.itemKey) || this.judgeIfOpen(_openKeys, item.items); if (shouldBeOpen) { break; } } } return shouldBeOpen; } handleCollapseChange() { const isCollapsed = !this.getState('isCollapsed'); if (!this._isControlledComponent('isCollapsed')) { this._adapter.setIsCollapsed(isCollapsed); } this._adapter.notifyCollapseChange(isCollapsed); } handleItemsChange(isChanged: boolean) { this._adapter.setItemsChanged(isChanged); } }