From bc3da76f48a040d8f153778f9cbeb56104f6c50d Mon Sep 17 00:00:00 2001 From: Jelf <353742991@qq.com> Date: Mon, 28 Feb 2022 04:19:51 +0800 Subject: [PATCH] fix(useTextSelection)!: listen to `selectionchange` event (#1194) --- packages/core/useTextSelection/demo.vue | 25 ++++--- packages/core/useTextSelection/index.ts | 89 ++++++++----------------- 2 files changed, 45 insertions(+), 69 deletions(-) diff --git a/packages/core/useTextSelection/demo.vue b/packages/core/useTextSelection/demo.vue index 35bd4ab6185..ff86a2278cb 100644 --- a/packages/core/useTextSelection/demo.vue +++ b/packages/core/useTextSelection/demo.vue @@ -1,19 +1,26 @@ diff --git a/packages/core/useTextSelection/index.ts b/packages/core/useTextSelection/index.ts index 080f031dfe0..2d6f632006e 100644 --- a/packages/core/useTextSelection/index.ts +++ b/packages/core/useTextSelection/index.ts @@ -1,70 +1,39 @@ -import type { MaybeRef } from '@vueuse/shared' -import { ref } from 'vue-demi' +import { computed, ref } from 'vue-demi' import { useEventListener } from '../useEventListener' -import { defaultWindow } from '../_configurable' -type Rect = Omit - -export interface UseTextSelectionState extends Rect { - text: string -} - -const initialRect: Rect = { - top: 0, - left: 0, - bottom: 0, - right: 0, - height: 0, - width: 0, -} - -const initialState: UseTextSelectionState = { - text: '', - ...initialRect, -} - -function getRectFromSelection(selection: Selection | null): Rect { - if (!selection || selection.rangeCount < 1) - return initialRect - - const range = selection.getRangeAt(0) - const { height, width, top, left, right, bottom } = range.getBoundingClientRect() - return { - height, - width, - top, - left, - right, - bottom, +function getRangesFromSelection(selection: Selection) { + const rangeCount = selection.rangeCount ?? 0 + const ranges = new Array(rangeCount) + for (let i = 0; i < rangeCount; i++) { + const range = selection.getRangeAt(i) + ranges[i] = range } + return ranges } -export function useTextSelection( - element?: MaybeRef, -) { - const state = ref(initialState) - - if (!defaultWindow?.getSelection) return state - - const onMouseup = () => { - const text = window.getSelection()?.toString() - if (text) { - const rect = getRectFromSelection(window.getSelection()) - state.value = { - ...state.value, - ...rect, - text, - } - } +/** + * Reactively track user text selection based on [`Window.getSelection`](https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection). + * + * @see https://vueuse.org/useTextSelection + */ +export function useTextSelection() { + const selection = ref(null) + const text = computed(() => selection.value?.toString() ?? '') + const ranges = computed(() => selection.value ? getRangesFromSelection(selection.value) : []) + const rects = computed(() => ranges.value.map(range => range.getBoundingClientRect())) + + function onSelectionChange() { + selection.value = null // trigger computed update + selection.value = window.getSelection() } - const onMousedown = () => { - state.value.text && (state.value = initialState) - window.getSelection()?.removeAllRanges() - } - useEventListener(element ?? document, 'mouseup', onMouseup) - useEventListener(document, 'mousedown', onMousedown) - return state + useEventListener(document, 'selectionchange', onSelectionChange) + return { + text, + rects, + ranges, + selection, + } } export type UseTextSelectionReturn = ReturnType