NHZEX / nxAdmin-soybean-admin

Soybean Admin Fork
https://soybeanjs.cn
MIT License
0 stars 0 forks source link

NaiveUi 增强方案备忘录 #2

Open NHZEX opened 4 months ago

NHZEX commented 4 months ago

模态窗拖动增强

参考: https://github.com/tusen-ai/naive-ui/issues/5501

方案一

/**
 *模态框拖动Hook
 *
 * @param {HTMLElement} draggableEl - 要拖动的元素。
 * @param {string} [childrenSelectors='.n-card-header'] - 用于查找要应用拖动事件的子元素的选择器。
 */
export const useDragModal = (draggableEl, childrenSelectors = '.n-card-header') => {
    const targetEl = draggableEl.querySelector(childrenSelectors);
    targetEl.style.cursor = 'move';
    mousedown(targetEl, draggableEl);
    mouseleave(targetEl);
    mouseup(targetEl);
  };
const mousedown = (targetEl, draggableEl) => {
  targetEl.onmousedown = event => {
    const viewportWidth = window.innerWidth || document.documentElement.clientWidth;
    const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
    const { width, height, x: minMoveX, y: minMoveY } = draggableEl.getBoundingClientRect();
    const maxMoveX = viewportWidth - width - minMoveX;
    const maxMoveY = viewportHeight - height - minMoveY;
    const { clientX: originX, clientY: originY } = event;
    let { left, top } = draggableEl.style;
    // getComputedStyle
    const styleLeft = left?.replace('px', '') ?? 0;
    const styleTop = top.replace('px', '') ?? 0;
    document.onmousemove = e => {
      const { clientX, clientY } = e;
      let moveX = clientX - originX;
      let moveY = clientY - originY;
      if (moveX > maxMoveX) {
        moveX = maxMoveX;
      } else if (-moveX > minMoveX) {
        moveX = -minMoveX;
      }
      if (moveY > maxMoveY) {
        moveY = maxMoveY;
      } else if (-moveY > minMoveY) {
        moveY = -minMoveY;
      }
      draggableEl.style.cssText += `;left:${+styleLeft + moveX}px;top:${+styleTop + moveY}px;`;
    };
  };
};
const mouseup = () => {
  document.onmouseup = () => {
    document.onmousemove = null;
  };
};
const mouseleave = targetEl => {
  targetEl.onmouseleave = () => (document.onmousemove = null);
};

// 使用方法: `<n-modal @after-enter="useDragModal($event)"></n-modal>`

方案二

export interface Options {
  /**
   * 禁用拖拽的类名
   * @default drag-disabled
   */
  disabledClass?: string
  /**
   * 最大搜索深度
   * @default 3
   */
  maxDepth?: number
}

export function modalDraggable(options?: Options) {
  const { disabledClass = 'drag-disabled', maxDepth = 3 } = options ?? {}

  const findModalHeader = (target: HTMLElement) => {
    let depth = 0

    while (target && depth < maxDepth) {
      if (target.classList.contains('n-card-header__close')) return

      if (target.classList.contains('n-card-header') && target.parentElement?.classList.contains('n-modal')) {
        if (target.parentElement?.classList.contains(disabledClass)) return

        return target
      }

      target = target.parentElement as HTMLElement
      depth += 1
    }
  }

  const handleMousedown = (originEvent: MouseEvent) => {
    const header = findModalHeader(originEvent.target as HTMLElement)
    if (!header) return
    const targetEl = header.parentElement as HTMLElement

    const { innerWidth, innerHeight } = window
    const userSelect = document.body.style.userSelect
    document.body.style.userSelect = 'none'

    const originLeft = parseFloat(targetEl.style.left) || 0
    const originTop = parseFloat(targetEl.style.top) || 0

    const handleMousemove = (moveEvent: MouseEvent) => {
      const left = originLeft + Math.min(innerWidth, Math.max(0, moveEvent.clientX)) - originEvent.clientX
      const top = originTop + Math.min(innerHeight, Math.max(0, moveEvent.clientY)) - originEvent.clientY
      targetEl.style.left = `${left}px`
      targetEl.style.top = `${top}px`
    }

    const handleMouseup = () => {
      document.body.style.userSelect = userSelect
      document.removeEventListener('mousemove', handleMousemove)
      document.removeEventListener('mouseup', handleMouseup)
    }

    document.addEventListener('mousemove', handleMousemove)
    document.addEventListener('mouseup', handleMouseup)
  }

  document.addEventListener('mousedown', handleMousedown)

  return () => document.removeEventListener('mousedown', handleMousedown)
}

// 把这个方法在main.js里面执行一下,所有的modal都可以拖动,需要禁止的地方加drag-disabled类名
NHZEX commented 4 months ago

ResizeObserver 使用

https://github.com/tusen-ai/naive-ui/issues/5084#issuecomment-1645024037

ResizeObserver 这个,感觉在这里 使用 hook 的方式好一些吧。这样就不需要包裹无渲染组件,少一层组件实例。还能避免 data-v-* 的问题

function useReizeObserver(target: MaybeRef<HTMLElement | null>, handler: (p: Param) => void) : () => void {
    const observer = new ResizeObserver(handler)

    const stop = () => {
        observer.disconnect()
    }

    watch(target, (t, o) => {
         o && observer.unobserve(o)
         t && observer.observe(t)
    }, { imediate: true })

    beforeUnmount(stop)

    return stop
}

// 在 setup 里
useResizeObserver(wrapperRef, handleContainerResize)
pinecone-squirrel commented 3 months ago

如果狭义的来理解 第一个不应该叫hooks,因为没有响应式数据