import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Button, Count, Flex, Menu, menuHelpers, Popover, SearchInput, createUID, useElement, useOuterEvent, useToaster, useAfterInitialEffect } from '@pega/cosmos-react-core';
import { cars, countries } from './Menu.mocks';
import { StyledFlag } from './Menu.styles';
export default {
    title: 'Core/Menu',
    component: Menu,
    args: {
        variant: 'drill-down',
        mode: 'action',
        itemLayout: 'stacked',
        showItemSecondary: true,
        showItemVisual: true,
        showItemCount: true,
        parentSelection: false,
        filterPattern: 'contains',
        infiniteScroll: false,
        noItems: false,
        emptyText: 'No items',
        useFlatData: false
    },
    argTypes: {
        variant: { options: ['drill-down', 'flyout'], control: { type: 'select' } },
        mode: { options: ['action', 'single-select', 'multi-select'], control: { type: 'select' } },
        itemLayout: { options: ['stacked', 'inline'], control: { type: 'select' } },
        showItemSecondary: { control: { type: 'boolean' } },
        showItemVisual: { control: { type: 'boolean' } },
        showItemCount: { control: { type: 'boolean' } },
        parentSelection: { control: { type: 'boolean' } },
        filterPattern: { options: ['contains', 'starts with'], control: { type: 'select' } },
        infiniteScroll: { control: { type: 'boolean' } },
        noItems: { control: { type: 'boolean' } },
        emptyText: { control: { type: 'text' } },
        useFlatData: { control: { type: 'boolean' } }
    }
};
export const MenuDemo = (args) => {
    const menuID = createUID();
    const menuListID = `${menuID}--list`;
    const data = args.useFlatData ? cars : countries;
    const searchEleRef = useRef(null);
    const { push } = useToaster();
    const [isOpen, setIsOpen] = useState(false);
    const [isLoading, setIsLoading] = useState(false);
    const [hasMore, setHasMore] = useState(true);
    const [items, setItems] = useState(args.infiniteScroll ? data.slice(0, 10) : data);
    const [buttonEl, setButtonEl] = useElement(null);
    const [popoverEl, setPopoverEl] = useElement(null);
    const [search, setSearch] = useState('');
    const selections = useMemo(() => menuHelpers.getSelected(items), [items]);
    const searchRegex = useMemo(() => {
        const escapedSearch = search.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
        return new RegExp(args.filterPattern === 'contains' ? escapedSearch : `^${escapedSearch}`, 'i');
    }, [search, args.filterPattern]);
    const itemsToRender = useMemo(() => {
        return menuHelpers.mapTree(search
            ? menuHelpers.flatten(items).filter(({ primary }) => {
                return searchRegex.test(primary);
            })
            : items, item => {
            let selectable = false;
            if (!item.items || args.parentSelection) {
                selectable = true;
            }
            return {
                ...item,
                visual: args.showItemVisual && item.visual ? <StyledFlag>{item.visual}</StyledFlag> : undefined,
                count: args.showItemCount && item.items ? item.items.length : undefined,
                secondary: args.showItemSecondary ? item.secondary : undefined,
                selected: selectable ? !!item.selected : undefined,
                tooltip: args.variant === 'flyout' ? item.tooltip : undefined
            };
        });
    }, [
        search,
        items,
        searchRegex,
        args.showItemVisual,
        args.showItemCount,
        args.showItemSecondary,
        args.variant,
        args.parentSelection
    ]);
    const resetMenu = useCallback(() => {
        setIsOpen(false);
        setItems(data.slice(0, 10));
        setSearch('');
    }, []);
    const onKeydown = useCallback(({ key }) => {
        if (key === 'Escape')
            resetMenu();
    }, [resetMenu]);
    const loadMore = useCallback((expandedItemId) => {
        if (!expandedItemId) {
            setIsLoading(true);
            setTimeout(() => {
                setIsLoading(false);
                setItems(prevItems => {
                    const newItems = data.slice(prevItems.length, prevItems.length + 10);
                    if (newItems.length === 0)
                        setHasMore(false);
                    return menuHelpers.appendTo(prevItems, newItems, expandedItemId);
                });
            }, 2000);
        }
    }, [data]);
    useOuterEvent('mousedown', [popoverEl, buttonEl], e => {
        if (args.variant === 'flyout') {
            const path = e.composedPath();
            const considerEvent = path.every(el => {
                return !(el instanceof HTMLElement && el.dataset.flyoutMenuParentId === menuID);
            });
            if (considerEvent) {
                resetMenu();
            }
        }
        else {
            resetMenu();
        }
    });
    useEffect(() => {
        document.addEventListener('keydown', onKeydown);
        return () => {
            document.removeEventListener('keydown', onKeydown);
        };
    }, []);
    useEffect(() => {
        if (isOpen) {
            searchEleRef?.current?.focus();
        }
    }, [isOpen, searchEleRef.current]);
    return (<Flex container={{ justify: 'center' }}>
      <Button ref={setButtonEl} onClick={() => setIsOpen(cur => !cur)} variant='primary'>
        Open the MENU
      </Button>

      <Popover show={isOpen} ref={setPopoverEl} target={buttonEl}>
        <Menu {...args} id={menuID} listId={menuListID} items={args.noItems ? [] : itemsToRender} itemLayout={args.itemLayout} accent={search ? searchRegex : undefined} mode={args.variant === 'drill-down' ? args.mode : 'action'} role='listbox' loading={isLoading} emptyText={args.emptyText} variant={args.variant} loadMore={args.infiniteScroll && hasMore ? loadMore : undefined} focusControlEl={searchEleRef.current || undefined} onItemClick={id => {
            if (args.mode === 'single-select' || args.mode === 'multi-select') {
                setItems(cur => menuHelpers.toggleSelected(cur, id, args.mode));
            }
            else {
                const clicked = menuHelpers.getItem(items, id);
                if (clicked) {
                    push({
                        id: `${Date.now()}`,
                        content: `${clicked.primary} added`
                    });
                }
                resetMenu();
            }
        }} header={args.variant === 'drill-down' && (<SearchInput ref={searchEleRef} onSearchChange={setSearch} value={search} role='searchbox' searchInputAriaLabel='Start typing to search.' aria-controls={menuListID}/>)} footer={args.variant === 'flyout' ? (<SearchInput ref={searchEleRef} onSearchChange={setSearch} value={search} role='searchbox' searchInputAriaLabel='Start typing to search.'/>) : ((args.mode === 'single-select' || args.mode === 'multi-select') && (<Flex container={{ justify: 'between' }}>
                  <Button onClick={resetMenu}>Cancel</Button>

                  <Button variant='primary' disabled={!selections.length} onClick={() => {
                resetMenu();
                push({
                    id: `${Date.now()}`,
                    content: `${selections.length} item${selections.length !== 1 ? 's' : ''} added`
                });
            }}>
                    Add
                    {selections.length > 0 && args.mode === 'multi-select' && (<>
                        &nbsp;<Count>{selections.length}</Count>
                      </>)}
                  </Button>
                </Flex>))}/>
      </Popover>
    </Flex>);
};
export const DepthSpecificSearchMenuDemo = (args) => {
    const menuID = createUID();
    const { push } = useToaster();
    const menuListID = `${menuID}--list`;
    const data = countries;
    const [buttonEl, setButtonEl] = useElement(null);
    const [popoverEl, setPopoverEl] = useElement(null);
    const searchEleRef = useRef(null);
    const menuHandle = useRef(null);
    const [isOpen, setIsOpen] = useState(false);
    const [expandedItemId, setExpandedItemId] = useState();
    const [items, setItems] = useState(data);
    const selections = useMemo(() => menuHelpers.getSelected(items), [items]);
    const [filterValue, setFilterValue] = useState('');
    const filterRegex = useMemo(() => {
        if (!filterValue)
            return;
        const escapedSearch = filterValue.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
        return new RegExp(args.filterPattern === 'contains' ? escapedSearch : `^${escapedSearch}`, 'i');
    }, [filterValue, args.filterPattern]);
    const itemsToRender = useMemo(() => {
        if (!filterRegex)
            return items;
        const expandedItemHasItems = expandedItemId
            ? menuHelpers.getItem(data, expandedItemId)?.items
            : undefined;
        if (expandedItemId) {
            if (!expandedItemHasItems)
                return [];
            const expandedItemSearchList = menuHelpers.flatten(expandedItemHasItems);
            return expandedItemSearchList
                .filter(({ primary }) => {
                return filterRegex.test(primary);
            })
                .map(item => {
                let selectable = false;
                if (!item.items || args.parentSelection) {
                    selectable = true;
                }
                return {
                    ...item,
                    visual: args.showItemVisual && item.visual ? (<StyledFlag>{item.visual}</StyledFlag>) : undefined,
                    secondary: args.showItemSecondary ? item.secondary : undefined,
                    selected: selectable ? !!item.selected : undefined
                };
            });
        }
        return menuHelpers.mapTree(filterValue
            ? menuHelpers.flatten(items).filter(({ primary }) => {
                return filterRegex.test(primary);
            })
            : items, item => {
            let selectable = false;
            if (!item.items || args.parentSelection) {
                selectable = true;
            }
            return {
                ...item,
                visual: args.showItemVisual && item.visual ? <StyledFlag>{item.visual}</StyledFlag> : undefined,
                secondary: args.showItemSecondary ? item.secondary : undefined,
                selected: selectable ? !!item.selected : undefined
            };
        });
    }, [filterValue, items, filterRegex, args.showItemVisual]);
    const resetMenu = useCallback(() => {
        setIsOpen(false);
        setItems(data.slice(0, 10));
        setFilterValue('');
    }, []);
    const onItemExpand = (id) => {
        setExpandedItemId(id);
    };
    const onItemCollapse = (id) => {
        const previouslyExpandedId = menuHelpers.getParentItem(items, id)?.id;
        setExpandedItemId(previouslyExpandedId);
    };
    const onKeydown = useCallback(({ key }) => {
        if (key === 'Escape')
            resetMenu();
    }, [resetMenu]);
    useOuterEvent('mousedown', [popoverEl, buttonEl], () => {
        resetMenu();
    });
    useEffect(() => {
        document.addEventListener('keydown', onKeydown);
        return () => {
            document.removeEventListener('keydown', onKeydown);
        };
    }, []);
    useEffect(() => {
        if (isOpen) {
            searchEleRef?.current?.focus();
        }
    }, [isOpen, searchEleRef.current]);
    useAfterInitialEffect(() => {
        if (!filterRegex && expandedItemId) {
            menuHandle.current?.expandTo(expandedItemId);
        }
    }, [filterValue]);
    return (<Flex container={{ justify: 'center' }}>
      <Button ref={setButtonEl} onClick={() => {
            setIsOpen(cur => !cur);
        }} variant='primary'>
        Open the MENU
      </Button>

      <Popover show={isOpen} ref={setPopoverEl} target={buttonEl}>
        <Menu {...args} id={menuID} listId={menuListID} items={args.noItems ? [] : itemsToRender} itemLayout={args.itemLayout} accent={filterValue ? filterRegex : undefined} mode={args.mode} role='listbox' emptyText={args.emptyText} focusControlEl={searchEleRef.current || undefined} onItemClick={id => {
            if (args.mode === 'single-select' || args.mode === 'multi-select') {
                setItems(cur => menuHelpers.toggleSelected(cur, id, args.mode));
            }
            else {
                const clicked = menuHelpers.getItem(items, id);
                if (clicked) {
                    push({
                        id: `${Date.now()}`,
                        content: `${clicked.primary} added`
                    });
                }
                resetMenu();
            }
        }} onItemExpand={onItemExpand} onItemCollapse={onItemCollapse} header={<SearchInput ref={searchEleRef} onSearchChange={setFilterValue} value={filterValue} role='searchbox' searchInputAriaLabel='Start typing to search.' aria-controls={menuListID}/>} footer={(args.mode === 'single-select' || args.mode === 'multi-select') && (<Flex container={{ justify: 'between' }}>
                <Button onClick={resetMenu}>Cancel</Button>

                <Button variant='primary' disabled={!selections.length} onClick={() => {
                resetMenu();
                push({
                    id: `${Date.now()}`,
                    content: `${selections.length} item${selections.length !== 1 ? 's' : ''} added`
                });
            }}>
                  Add
                  {selections.length > 0 && args.mode === 'multi-select' && (<>
                      &nbsp;<Count>{selections.length}</Count>
                    </>)}
                </Button>
              </Flex>)} handleRef={menuHandle}/>
      </Popover>
    </Flex>);
};
DepthSpecificSearchMenuDemo.argTypes = {
    showItemCount: { table: { disable: true } },
    variant: { table: { disable: true } },
    infiniteScroll: { table: { disable: true } },
    useFlatData: { table: { disable: true } }
};
export const FlatMenuDemo = (args) => {
    return (<MenuDemo variant={args.variant} mode={args.mode} itemLayout={args.itemLayout} showItemSecondary={args.showItemSecondary} showItemVisual={args.showItemVisual} showItemCount={args.showItemCount} parentSelection={args.parentSelection} filterPattern={args.filterPattern} infiniteScroll={args.infiniteScroll} noItems={args.noItems} emptyText={args.emptyText} useFlatData/>);
};
FlatMenuDemo.argTypes = {
    useFlatData: { table: { disable: true } }
};
export const WithoutFocusControlEl = (args) => {
    const menuID = createUID();
    const menuListID = `${menuID}--list`;
    const buttonRef = useRef(null);
    const [showMenu, setShowMenu] = useState(false);
    const [items, setItems] = useState(cars);
    const itemsToRender = useMemo(() => {
        return menuHelpers.mapTree(items, item => {
            let selectable = false;
            if (!item.items || args.parentSelection) {
                selectable = true;
            }
            return {
                ...item,
                visual: args.showItemVisual && item.visual ? <StyledFlag>{item.visual}</StyledFlag> : undefined,
                count: args.showItemCount && item.items ? item.items.length : undefined,
                secondary: args.showItemSecondary ? item.secondary : undefined,
                selected: selectable ? !!item.selected : undefined
            };
        });
    }, [items, args.showItemVisual, args.showItemCount, args.showItemSecondary]);
    return (<Flex container={{ gap: 2, direction: 'column', alignItems: 'start' }} style={{ maxWidth: '500px' }}>
      <Button ref={buttonRef} onClick={() => {
            setShowMenu(!showMenu);
        }}>
        {showMenu ? 'Hide' : 'Show'}
      </Button>

      {showMenu && (<Menu id={menuID} listId={menuListID} items={itemsToRender} itemLayout={args.itemLayout} mode='multi-select' role='listbox' onItemClick={id => {
                setItems(cur => menuHelpers.toggleSelected(cur, id, args.mode));
            }}/>)}
    </Flex>);
};
WithoutFocusControlEl.argTypes = {
    filterPattern: { table: { disable: true } },
    variant: { table: { disable: true } },
    infiniteScroll: { table: { disable: true } },
    noItems: { table: { disable: true } },
    emptyText: { table: { disable: true } },
    useFlatData: { table: { disable: true } }
};
//# sourceMappingURL=Menu.stories.jsx.map