<script lang="tsx">
    import type { CSSProperties, FunctionalComponent, PropType } from 'vue'
    import { computed, defineComponent, nextTick, onMounted, onUnmounted, ref, unref } from 'vue'
    import Icon from '/@/components/Icon'
    import { Divider, Menu } from 'ant-design-vue'
    import type { Axis, ContextMenuItem, ItemContentProps } from './typing'

    const prefixCls = 'context-menu'
    const props = {
        width: { type: Number, default: 156 },
        customEvent: { type: Object as PropType<Event>, default: null },
        styles: { type: Object as PropType<CSSProperties> },
        showIcon: { type: Boolean, default: true },
        axis: {
            // The position of the right mouse button click
            type: Object as PropType<Axis>,
            default() {
                return { x: 0, y: 0 }
            },
        },
        items: {
            // The most important list, if not, will not be displayed
            type: Array as PropType<ContextMenuItem[]>,
            default() {
                return []
            },
        },
    }
    const ItemContent: FunctionalComponent<ItemContentProps> = (props) => {
        const { item } = props
        return (
            <span style="display: inline-block; width: 100%; " class="px-4" onClick={props.handler.bind(null, item)}>
                {props.showIcon && item.icon && <Icon class="mr-2" icon={item.icon} />}
                <span>{item.label}</span>
            </span>
        )
    }
    export default defineComponent({
        name: 'ContextMenu',
        props,
        setup(props) {
            const wrapRef = ref(null)
            const showRef = ref(false)
            const getStyle = computed((): CSSProperties => {
                const { axis, items, styles, width } = props
                const { x, y } = axis || { x: 0, y: 0 }
                const menuHeight = (items || []).length * 40
                const menuWidth = width
                const body = document.body
                const left = body.clientWidth < x + menuWidth ? x - menuWidth : x
                const top = body.clientHeight < y + menuHeight ? y - menuHeight : y
                return {
                    ...styles,
                    position: 'absolute',
                    width: `${width}px`,
                    left: `${left + 1}px`,
                    top: `${top + 1}px`,
                    zIndex: 9999,
                }
            })
            onMounted(() => {
                nextTick(() => (showRef.value = true))
            })
            onUnmounted(() => {
                const el = unref(wrapRef)
                el && document.body.removeChild(el)
            })
            function handleAction(item: ContextMenuItem, e: MouseEvent) {
                const { handler, disabled } = item
                if (disabled) {
                    return
                }
                showRef.value = false
                e?.stopPropagation()
                e?.preventDefault()
                handler?.()
            }
            function renderMenuItem(items: ContextMenuItem[]) {
                const visibleItems = items.filter((item) => !item.hidden)
                return visibleItems.map((item) => {
                    const { disabled, label, children, divider = false } = item
                    const contentProps = {
                        item,
                        handler: handleAction,
                        showIcon: props.showIcon,
                    }
                    if (!children || children.length === 0) {
                        return (
                            <>
                                <Menu.Item disabled={disabled} class={`${prefixCls}__item`} key={label}>
                                    <ItemContent {...contentProps} />
                                </Menu.Item>
                                {divider ? <Divider key={`d-${label}`} /> : null}
                            </>
                        )
                    }
                    if (!unref(showRef)) return null
                    return (
                        <Menu.SubMenu key={label} disabled={disabled} popupClassName={`${prefixCls}__popup`}>
                            {{
                                title: () => <ItemContent {...contentProps} />,
                                default: () => renderMenuItem(children),
                            }}
                        </Menu.SubMenu>
                    )
                })
            }
            return () => {
                if (!unref(showRef)) {
                    return null
                }
                const { items } = props
                return (
                    <div class={prefixCls}>
                        <Menu inlineIndent={12} mode="vertical" ref={wrapRef} style={unref(getStyle)}>
                            {renderMenuItem(items)}
                        </Menu>
                    </div>
                )
            }
        },
    })
</script>

<style lang="less">
    @default-height: 42px !important;
    @small-height: 36px !important;
    @large-height: 36px !important;
    .item-style() {
        li {
            display: inline-block;
            width: 100%;
            height: @default-height;
            margin: 0 !important;
            line-height: @default-height;

            span {
                line-height: @default-height;
            }

            > div {
                margin: 0 !important;
            }

            &:not(.ant-menu-item-disabled):hover {
                color: @text-color-base;
                background-color: @item-hover-bg;
            }
        }
    }

    .context-menu {
        position: fixed;
        top: 0;
        left: 0;
        z-index: 200;
        display: block;
        width: 156px;
        margin: 0;
        list-style: none;
        background-color: @component-background;
        border: 1px solid rgb(0 0 0 / 8%);
        border-radius: 0.25rem;
        box-shadow: 0 2px 2px 0 rgb(0 0 0 / 14%), 0 3px 1px -2px rgb(0 0 0 / 10%), 0 1px 5px 0 rgb(0 0 0 / 6%);
        background-clip: padding-box;
        user-select: none;

        &__item {
            margin: 0 !important;
        }
        .item-style();

        .ant-divider {
            margin: 0;
        }

        &__popup {
            .ant-divider {
                margin: 0;
            }
            .item-style();
        }

        .ant-menu-submenu-title,
        .ant-menu-item {
            padding: 0 !important;
        }
    }
</style>
