123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- /* 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';
- 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<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
- 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<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<NavigationAdapter<P, S>, P, S> {
- constructor(adapter: NavigationAdapter<P, S>) {
- super({ ...adapter });
- }
- /* istanbul ignore next */
- 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, formattedItems } = this.getCalcState();
- const parentSelectKeys = this.selectLevelZeroParentKeys(itemKeysMap, willSelectedKeys);
- willSelectedKeys = willSelectedKeys.concat(parentSelectKeys);
- if (lifecycle === 'constructor') {
- return {
- selectedKeys: willSelectedKeys,
- itemKeysMap,
- openKeys: willOpenKeys,
- items: formattedItems,
- };
- } else {
- this._adapter.updateSelectedKeys(willSelectedKeys);
- this._adapter.setItemKeysMap(itemKeysMap);
- this._adapter.updateOpenKeys(willOpenKeys);
- this._adapter.updateItems(formattedItems);
- this._adapter.setItemsChanged(true);
- }
- return undefined;
- }
- /**
- * Get the state to be calculated
- */
- getCalcState() {
- const { itemKeysMap, formattedItems } = this.getFormattedItems();
- const willOpenKeys = this.getWillOpenKeys(itemKeysMap);
- return { itemKeysMap, willOpenKeys, formattedItems };
- }
- /**
- * Calculate formatted items and itemsKeyMap
- */
- getFormattedItems() {
- const { items, children } = this.getProps();
- const formattedItems = this.formatItems(items);
- const willHandleItems = Array.isArray(items) && items.length ? formattedItems : children;
- const itemKeysMap = NavigationFoundation.buildItemKeysMap(willHandleItems);
- return {
- itemKeysMap,
- formattedItems
- };
- }
- /**
- * 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 formattedItems = [];
- for (const item of items) {
- formattedItems.push(new NavItem(item));
- }
- return formattedItems;
- }
- handleSelect(data: OnSelectData) {
- this._adapter.notifySelect(data);
- }
- /* istanbul ignore next */
- 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);
- }
- }
|