import { Teleport, computed, defineComponent, nextTick, onMounted, ref, watch, onActivated, onDeactivated, createVNode as _createVNode, vShow as _vShow, mergeProps as _mergeProps, withDirectives as _withDirectives } from "vue"; import { pick, addUnit, closest, createNamespace, makeNumberProp, makeStringProp, windowWidth, windowHeight } from "../utils/index.mjs"; import { useRect, useEventListener } from "@vant/use"; import { useTouch } from "../composables/use-touch.mjs"; import Icon from "../icon/index.mjs"; const floatingBubbleProps = { gap: makeNumberProp(24), icon: String, axis: makeStringProp("y"), magnetic: String, offset: { type: Object, default: () => ({ x: -1, y: -1 }) }, teleport: { type: [String, Object], default: "body" } }; const [name, bem] = createNamespace("floating-bubble"); var stdin_default = defineComponent({ name, inheritAttrs: false, props: floatingBubbleProps, emits: ["click", "update:offset", "offsetChange"], setup(props, { slots, emit, attrs }) { const rootRef = ref(); const state = ref({ x: 0, y: 0, width: 0, height: 0 }); const boundary = computed(() => ({ top: props.gap, right: windowWidth.value - state.value.width - props.gap, bottom: windowHeight.value - state.value.height - props.gap, left: props.gap })); const dragging = ref(false); let initialized = false; const rootStyle = computed(() => { const style = {}; const x = addUnit(state.value.x); const y = addUnit(state.value.y); style.transform = `translate3d(${x}, ${y}, 0)`; if (dragging.value || !initialized) { style.transition = "none"; } return style; }); const updateState = () => { if (!show.value) return; const { width, height } = useRect(rootRef.value); const { offset } = props; state.value = { x: offset.x > -1 ? offset.x : windowWidth.value - width - props.gap, y: offset.y > -1 ? offset.y : windowHeight.value - height - props.gap, width, height }; }; const touch = useTouch(); let prevX = 0; let prevY = 0; const onTouchStart = (e) => { touch.start(e); dragging.value = true; prevX = state.value.x; prevY = state.value.y; }; const onTouchMove = (e) => { e.preventDefault(); touch.move(e); if (props.axis === "lock") return; if (!touch.isTap.value) { if (props.axis === "x" || props.axis === "xy") { let nextX = prevX + touch.deltaX.value; if (nextX < boundary.value.left) nextX = boundary.value.left; if (nextX > boundary.value.right) nextX = boundary.value.right; state.value.x = nextX; } if (props.axis === "y" || props.axis === "xy") { let nextY = prevY + touch.deltaY.value; if (nextY < boundary.value.top) nextY = boundary.value.top; if (nextY > boundary.value.bottom) nextY = boundary.value.bottom; state.value.y = nextY; } const offset = pick(state.value, ["x", "y"]); emit("update:offset", offset); } }; useEventListener("touchmove", onTouchMove, { target: rootRef }); const onTouchEnd = () => { dragging.value = false; nextTick(() => { if (props.magnetic === "x") { const nextX = closest([boundary.value.left, boundary.value.right], state.value.x); state.value.x = nextX; } if (props.magnetic === "y") { const nextY = closest([boundary.value.top, boundary.value.bottom], state.value.y); state.value.y = nextY; } if (!touch.isTap.value) { const offset = pick(state.value, ["x", "y"]); emit("update:offset", offset); if (prevX !== offset.x || prevY !== offset.y) { emit("offsetChange", offset); } } }); }; const onClick = (e) => { if (touch.isTap.value) emit("click", e); else e.stopPropagation(); }; onMounted(() => { updateState(); nextTick(() => { initialized = true; }); }); watch([windowWidth, windowHeight, () => props.gap, () => props.offset], updateState, { deep: true }); const show = ref(true); onActivated(() => { show.value = true; }); onDeactivated(() => { if (props.teleport) { show.value = false; } }); return () => { const Content = _withDirectives(_createVNode("div", _mergeProps({ "class": bem(), "ref": rootRef, "onTouchstartPassive": onTouchStart, "onTouchend": onTouchEnd, "onTouchcancel": onTouchEnd, "onClickCapture": onClick, "style": rootStyle.value }, attrs), [slots.default ? slots.default() : _createVNode(Icon, { "name": props.icon, "class": bem("icon") }, null)]), [[_vShow, show.value]]); return props.teleport ? _createVNode(Teleport, { "to": props.teleport }, { default: () => [Content] }) : Content; }; } }); export { stdin_default as default, floatingBubbleProps };