Skip to content

Commit

Permalink
feat:Dropdown 组件的 split-button 功能
Browse files Browse the repository at this point in the history
  • Loading branch information
EricWXY committed Apr 13, 2024
1 parent fdfcf31 commit 6f4444e
Show file tree
Hide file tree
Showing 49 changed files with 124 additions and 57,212 deletions.
2 changes: 1 addition & 1 deletion packages/components/Button/Button.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ defineExpose({
class="er-button"
:class="{
[`er-button--${type}`]: type,
[`er-button--${size}`]: size,
[`er-button--${size}`]: size != null && size != 'default',
'is-plain': plain,
'is-round': round,
'is-circle': circle,
Expand Down
4 changes: 2 additions & 2 deletions packages/components/Button/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type Component } from "vue";
export type ButtonType = "primary" | "success" | "warning" | "danger" | "info";
export type NativeType = "button" | "submit" | "reset";
export type ButtonSize = "large" | "small";
export type ButtonSize = "default" | "large" | "small";

export interface ButtonProps {
tag?: string | Component;
Expand All @@ -23,7 +23,7 @@ export interface ButtonProps {
export interface ButtonGroupProps {
size?: ButtonSize;
type?: ButtonType;
disabled?: boolean
disabled?: boolean;
}

export interface ButtonGroupContext {
Expand Down
16 changes: 9 additions & 7 deletions packages/components/Dropdown/Dropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ import type {
import { useId, useDisabledStyle } from "@eric-ui/hooks";
import { DROPDOWN_CTX_KEY } from "./constants";
import type { TooltipInstance } from "../Tooltip/types";
import type { ButtonInstance } from "../Button/types";
import ErButton from "../Button/Button.vue";
import ErButtonGroup from "../Button/ButtonGroup.vue";
import { type ButtonInstance, ErButton, ErButtonGroup } from "../Button";
import ErIcon from "../Icon/Icon.vue";
import DropdownItem from "./DropdownItem.vue";
import Tooltip from "../Tooltip/Tooltip.vue";
Expand Down Expand Up @@ -49,7 +47,6 @@ function handleItemClick(e: DropdownItemProps) {
}
useDisabledStyle(slots, props.disabled);
defineExpose<DropdownInstance>({
open: () => tooltipRef.value?.show(),
close: () => tooltipRef.value?.hide(),
Expand All @@ -65,12 +62,17 @@ provide<DropdownContext>(DROPDOWN_CTX_KEY, {
<tooltip
ref="tooltipRef"
v-bind="tooltipProps"
:virtual-ref="triggerRef"
:virtual-triggering="splitButton"
:virtual-ref="triggerRef"
@visible-change="$emit('visible-change', $event)"
>
<er-button-group :disabled="disabled" v-if="splitButton">
<er-button>
<er-button-group
:type="type"
:size="size"
:disabled="disabled"
v-if="splitButton"
>
<er-button @click="$emit('click', $event)">
<slot name="default"></slot>
</er-button>
<er-button ref="triggerRef">
Expand Down
3 changes: 3 additions & 0 deletions packages/components/Dropdown/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { VNode } from "vue";
import type { TooltipProps } from "../Tooltip/types";
import type { ButtonType, ButtonSize } from "../Button/types";

export type DropdownCommand = string | number;

Expand All @@ -11,6 +12,8 @@ export interface DropdownItemProps {
}

export interface DropdownProps extends TooltipProps {
type?: ButtonType;
size?: ButtonSize;
items?: DropdownItemProps[];
hideOnClick?: boolean;
splitButton?: boolean;
Expand Down
116 changes: 47 additions & 69 deletions packages/components/Tooltip/Tooltip.vue
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
<script setup lang="ts">
import type { TooltipProps, TooltipEmits, TooltipInstance } from "./types";
import { createPopper, type Instance } from "@popperjs/core";
import {
ref,
reactive,
watch,
watchEffect,
onUnmounted,
computed,
onMounted,
// isVNode,
type WatchStopHandle,
// type VNode,
} from "vue";
import { bind, debounce, each, isElement, type DebouncedFunc } from "lodash-es";
import { ref, watch, watchEffect, onUnmounted, computed, type Ref } from "vue";
import { bind, debounce, type DebouncedFunc } from "lodash-es";
import { useClickOutside } from "@eric-ui/hooks";
import type { TooltipProps, TooltipEmits, TooltipInstance } from "./types";
import type { ButtonInstance } from "../Button";
import useEvenstToTiggerNode from "./useEventsToTiggerNode";
defineOptions({
name: "ErTooltip",
});
interface _TooltipProps extends TooltipProps {
virtualRef?: any;
virtualRef?: HTMLElement | ButtonInstance | void;
virtualTriggering?: boolean;
}
Expand All @@ -33,16 +26,25 @@ const props = withDefaults(defineProps<_TooltipProps>(), {
hideTimeout: 150,
});
const emits = defineEmits<TooltipEmits>();
const isOpen = ref(false);
const visible = ref(false);
const events: Ref<Record<string, EventListener>> = ref({});
const outerEvents: Ref<Record<string, EventListener>> = ref({});
const dropdownEvents: Ref<Record<string, EventListener>> = ref({});
const containerNode = ref<HTMLElement>();
const popperNode = ref<HTMLElement>();
const triggerNode = ref<HTMLElement>();
let popperInstance: null | Instance;
let events: Record<string, any> = reactive({});
let outerEvents: Record<string, any> = reactive({});
let dropdownEvents: Record<string, any> = reactive({});
const _triggerNode = ref<HTMLElement>();
const triggerNode = computed(() => {
if (props.virtualTriggering)
return (
(props.virtualRef as ButtonInstance)?.ref ??
(props.virtualRef as HTMLElement) ??
_triggerNode.value
);
return _triggerNode.value;
});
const popperOptions = computed(() => ({
placement: props.placement,
Expand Down Expand Up @@ -79,43 +81,44 @@ function closeFinal() {
}
function togglePopper() {
isOpen.value ? closeFinal() : openFinal();
visible.value ? closeFinal() : openFinal();
}
function setVisible(val: boolean) {
if (props.disabled) return;
isOpen.value = val;
visible.value = val;
emits("visible-change", val);
}
function attachEvents() {
if (props.disabled || props.manual) return;
if (props.trigger === "hover") {
events["mouseenter"] = openFinal;
outerEvents["mouseleave"] = closeFinal;
dropdownEvents["mouseenter"] = openFinal;
events.value["mouseenter"] = openFinal;
outerEvents.value["mouseleave"] = closeFinal;
dropdownEvents.value["mouseenter"] = openFinal;
return;
}
if (props.trigger === "click") {
events["click"] = togglePopper;
events.value["click"] = togglePopper;
}
if (props.trigger === "contextmenu") {
events["contextmenu"] = (e: MouseEvent) => {
events.value["contextmenu"] = (e) => {
e.preventDefault();
openFinal();
};
}
}
let popperInstance: null | Instance;
function destroyPopperInstance() {
popperInstance?.destroy();
popperInstance = null;
}
function resetEvents() {
events = {};
outerEvents = {};
dropdownEvents = {};
events.value = {};
outerEvents.value = {};
dropdownEvents.value = {};
attachEvents();
}
Expand All @@ -134,11 +137,11 @@ const hide: TooltipInstance["hide"] = function () {
useClickOutside(containerNode, () => {
emits("click-outside");
if (props.trigger === "hover" || props.manual) return;
isOpen.value && closeFinal();
visible.value && closeFinal();
});
watch(
isOpen,
visible,
(val) => {
if (!val) return;
Expand All @@ -157,9 +160,9 @@ watch(
() => props.manual,
(isManual) => {
if (isManual) {
events = {};
outerEvents = {};
dropdownEvents = {};
events.value = {};
outerEvents.value = {};
dropdownEvents.value = {};
return;
}
attachEvents();
Expand All @@ -179,7 +182,7 @@ watch(
(val, oldVal) => {
if (val === oldVal) return;
openDebounce?.cancel();
isOpen.value = false;
visible.value = false;
emits("visible-change", false);
resetEvents();
}
Expand All @@ -190,38 +193,13 @@ watchEffect(() => {
closeDebounce = debounce(bind(setVisible, null, false), closeDelay.value);
});
let watchVirtualRefStopHandle: WatchStopHandle | void;
let watchEventsStopHandle: WatchStopHandle | void;
onMounted(() => {
watchVirtualRefStopHandle = watch(
() => props.virtualRef,
(virtualRef) => {
if (virtualRef && props.virtualTriggering) {
triggerNode.value = virtualRef;
}
},
{
immediate: true,
}
);
watchEventsStopHandle = watch(triggerNode, (triggerRef) => {
const el = (triggerRef as any)?.$el;
if (!props.virtualTriggering) return;
console.log(popperOptions.value);
if (isElement(el)) {
each(events, (fn, event) => {
el?.addEventListener(event, fn);
});
}
});
useEvenstToTiggerNode(props, triggerNode, events, () => {
openDebounce?.cancel();
setVisible(false);
});
onUnmounted(() => {
popperInstance?.destroy();
watchEventsStopHandle?.();
watchVirtualRefStopHandle?.();
destroyPopperInstance();
});
defineExpose<TooltipInstance>({
Expand All @@ -234,7 +212,7 @@ defineExpose<TooltipInstance>({
<div class="er-tooltip" ref="containerNode" v-on="outerEvents">
<div
class="er-tooltip__trigger"
ref="triggerNode"
ref="_triggerNode"
v-on="events"
v-if="!virtualTriggering"
>
Expand All @@ -247,7 +225,7 @@ defineExpose<TooltipInstance>({
class="er-tooltip__popper"
ref="popperNode"
v-on="dropdownEvents"
v-if="isOpen"
v-if="visible"
>
<slot name="content">
{{ content }}
Expand Down
7 changes: 0 additions & 7 deletions packages/components/Tooltip/TooltipContent.vue

This file was deleted.

Empty file.
1 change: 0 additions & 1 deletion packages/components/Tooltip/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
}

.er-tooltip__popper {
position: absolute;
background: var(--er-popover-bg-color);
border-radius: var(--er-popover-border-radius);
border: 1px solid var(--er-popover-border-color);
Expand Down
61 changes: 61 additions & 0 deletions packages/components/Tooltip/useEventsToTiggerNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { each, isElement } from "lodash-es";
import { onMounted, onUnmounted, watch } from "vue";
import type { ComputedRef, Ref, WatchStopHandle } from "vue";
import type { TooltipProps } from "./types";

export function useEvenstToTiggerNode(
props: TooltipProps & { virtualTriggering?: boolean },
triggerNode: ComputedRef<HTMLElement | undefined>,
events: Ref<Record<string, EventListener>>,
closeMethod: () => void
) {
let watchEventsStopHandle: WatchStopHandle | void;
let watchTriggerNodeStopHandle: WatchStopHandle | void;

const _eventHandleMap = new Map();

const _bindEventToVirtualTiggerNode = () => {
const el = triggerNode.value;
isElement(el) &&
each(events.value, (fn, event) => {
_eventHandleMap.set(event, fn);
el?.addEventListener(event as keyof HTMLElementEventMap, fn);
});
};
const _unbindEventToVirtualTiggerNode = () => {
const el = triggerNode.value;
isElement(el) &&
each(
["mouseenter", "click", "contextmenu"],
(key) =>
_eventHandleMap.has(key) &&
el?.removeEventListener(key, _eventHandleMap.get(key))
);
};

onMounted(() => {
watchTriggerNodeStopHandle = watch(
triggerNode,
() => props.virtualTriggering && _bindEventToVirtualTiggerNode(),
{ immediate: true }
);

watchEventsStopHandle = watch(
events,
() => {
if (!props.virtualTriggering) return;
_unbindEventToVirtualTiggerNode();
_bindEventToVirtualTiggerNode();
closeMethod();
},
{ deep: true }
);
});

onUnmounted(() => {
watchTriggerNodeStopHandle?.();
watchEventsStopHandle?.();
});
}

export default useEvenstToTiggerNode;
Loading

0 comments on commit 6f4444e

Please sign in to comment.