React-dnd: Cara untuk menyeret ke luar ke iframe

Dibuat pada 1 Agu 2019  ·  6Komentar  ·  Sumber: react-dnd/react-dnd

Apakah permintaan fitur Anda terkait dengan masalah?
React membatasi drag-and-drop dalam satu dokumen HTML. Entah bagaimana kita perlu menyeret sesuatu di luar ke iframe di dalam.

Jelaskan solusi yang Anda inginkan
Saya menemukan bahwa dnd HTML5backend hanya memicu panggilan balik seret di jendela. Jadi jika kita bisa membuat komponen di dalam iframe memicu panggilan balik tersebut. Dan React.createPortal membuat komponen di dalam iframe memiliki DndProvider yang sama dengan di luar. Untungnya, itu berhasil. Tetapi saya khawatir tentang beberapa masalah yang tidak saya perhatikan. Dan kodenya tidak begitu nyaman dengan HTML5Backend.

import React, {
  useRef,
  useEffect,
  useContext,
  forwardRef,
} from 'react';
import classnames from 'classnames';
import { DndContext } from 'react-dnd';

// Frame base on createPortal inside iframe dom.
import Frame from 'react-frame-component';

const events = [
  ['dragstart', 'handleTopDragStart', false],
  ['dragstart', 'handleTopDragStartCapture', true],
  ['dragend', 'handleTopDragEndCapture', true],
  ['dragenter', 'handleTopDragEnter', false],
  ['dragenter', 'handleTopDragEnterCapture', true],
  ['dragleave', 'handleTopDragLeaveCapture', true],
  ['dragover', 'handleTopDragOver', false],
  ['dragover', 'handleTopDragOverCapture', true],
  ['drop', 'handleTopDrop', false],
  ['drop', 'handleTopDropCapture', true],
];

export const DndFrame = forwardRef((props = {}, ref) => {
  const container = useRef(null);
  const dndValue = useContext(DndContext) || {};

  const { dragDropManager: { backend = {} } = {} } = dndValue;
  const { children, className, containerProps = {}, ...others } = props;

  const cls = classnames({
    'dnd-frame': true,
    [className]: !!className,
  });

  useEffect(() => {
    const { current } = container;

    if (!current) {
      return;
    }

    // this make callback run in outside window
    events.forEach((eventArgs = []) => {
      const [name, callbackName, capture = false] = eventArgs;

      const callback = backend[callbackName] && backend[callbackName].bind(backend);

      current.addEventListener(name, (...args) => {
        callback && callback(...args);
      }, capture);
    });
  }, [container.current]);

  return (
    <Frame className={cls} ref={ref} {...others}>
      <div className="dnd-frame-container" ref={container} {...containerProps}>
        { children }
      </div>
    </Frame>
  );
});

Jelaskan alternatif yang telah Anda pertimbangkan
Mungkin Anda dapat mengekspor beberapa opsi dengan HTML5Backend.

design decisions enhancement pinned

Komentar yang paling membantu

Saya menemukan solusi yang lebih mudah untuk menyederhanakan kode ini, tetapi saya tidak yakin apakah ini solusi yang lebih baik. Berharap dapat membantu Anda untuk memperbaiki masalah ini.

import React, { useContext, useEffect } from 'react';
import DndProvider, { DndContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import Frame, { FrameContext } from 'react-frame-component';

const DndFrame = ({ children }) => {
  const { dragDropManager } = useContext(DndContext);
  const { window } = useContext(FrameContext);

  useEffect(() => {
    dragDropManager.getBackend().addEventListeners(window);
  });

  return children;
};

const Example = () => (
  <DndProvider backend={HTML5Backend}>
    <Frame>
       <DndFrame>
          <div>...</div>
       </DndFrame>
    </Frame>
  </DndProvider>
);

Semua 6 komentar

Saya menemukan solusi yang lebih mudah untuk menyederhanakan kode ini, tetapi saya tidak yakin apakah ini solusi yang lebih baik. Berharap dapat membantu Anda untuk memperbaiki masalah ini.

import React, { useContext, useEffect } from 'react';
import DndProvider, { DndContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import Frame, { FrameContext } from 'react-frame-component';

const DndFrame = ({ children }) => {
  const { dragDropManager } = useContext(DndContext);
  const { window } = useContext(FrameContext);

  useEffect(() => {
    dragDropManager.getBackend().addEventListeners(window);
  });

  return children;
};

const Example = () => (
  <DndProvider backend={HTML5Backend}>
    <Frame>
       <DndFrame>
          <div>...</div>
       </DndFrame>
    </Frame>
  </DndProvider>
);

@HsuTing Lebih mudah dan lebih baik. Tapi masih terikat pada antarmuka HTML5Backend. Jika HTML5Backend mengubah addEventListeners => addListeners, web kita akan rusak

Ide yang sangat keren, saya akan bermain-main dengannya ketika saya kembali dari pendakian

Pada Selasa, 6 Agustus 2019, 23:43 晓爽[email protected] menulis:

@HsuTing https://github.com/HsuTing Lebih mudah dan lebih baik. Tetapi tetap saja
terikat ke antarmuka HTML5Backend. Jika HTML5Backend mengubah addEventListeners
=> addListeners, web kita akan rusak


Anda menerima ini karena Anda ditugaskan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/react-dnd/react-dnd/issues/1496?email_source=notifications&email_token=AAA3XCDZJS3V5E7YISDRUC3QDJVJLA5CNFSM4IIMWOQ2YY3PNVWWK3TUL52HS4DFVREXG43VMXVBW63LDN
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AAA3XCGHNHO3M3CSL5VRUQTQDJVJLANCNFSM4IIMWOQQ
.

Saya menemukan solusi yang lebih mudah untuk menyederhanakan kode ini, tetapi saya tidak yakin apakah ini solusi yang lebih baik. Berharap dapat membantu Anda untuk memperbaiki masalah ini.

import React, { useContext, useEffect } from 'react';
import DndProvider, { DndContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import Frame, { FrameContext } from 'react-frame-component';

const DndFrame = ({ children }) => {
  const { dragDropManager } = useContext(DndContext);
  const { window } = useContext(FrameContext);

  useEffect(() => {
    dragDropManager.getBackend().addEventListeners(window);
  });

  return children;
};

const Example = () => (
  <DndProvider backend={HTML5Backend}>
    <Frame>
       <DndFrame>
          <div>...</div>
       </DndFrame>
    </Frame>
  </DndProvider>
);

Ini adalah pendekatan yang bagus dan berhasil untuk saya.

Saya menemukan solusi yang lebih mudah untuk menyederhanakan kode ini, tetapi saya tidak yakin apakah ini solusi yang lebih baik. Berharap dapat membantu Anda untuk memperbaiki masalah ini.

import React, { useContext, useEffect } from 'react';
import DndProvider, { DndContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import Frame, { FrameContext } from 'react-frame-component';

const DndFrame = ({ children }) => {
  const { dragDropManager } = useContext(DndContext);
  const { window } = useContext(FrameContext);

  useEffect(() => {
    dragDropManager.getBackend().addEventListeners(window);
  });

  return children;
};

const Example = () => (
  <DndProvider backend={HTML5Backend}>
    <Frame>
       <DndFrame>
          <div>...</div>
       </DndFrame>
    </Frame>
  </DndProvider>
);

Saat mencoba dengan TypeScript saya mendapatkan kesalahan Property 'addEventListeners' does not exist on type 'Backend'

apakah ada cara untuk memperbaikinya di TS?

addEventListeners tidak diekspos pada antarmuka Backend; dan saya tidak ingin mengeksposnya karena ini khusus DOM/Browser. Saya pikir apa yang mungkin harus kita lakukan adalah sesuatu seperti:

// detect if SSR mode
const DEFAULT_GLOBAL_CONTEXT = typeof window !== 'undefined' ? window : global

const DndFrame = ({ children, globalContext = DEFAULT_GLOBAL_CONTEXT}) => {
  const { dragDropManager } = useContext(DndContext);
  const { window } = useContext(FrameContext);
  const backend = useMemo(() => dragDropManager.getBackend(), [dragDropManager])

  useEffect(() => {
     // This will required adding an initialize() method to the Backend interface.
    // Backend constructors will have to thunk over to it. It could replace constructors, but that would be semver major.
    backend.initialize(dragDropManager, globalContext)
    backend.setup()
  }, [globalContext]);

  return children;
};

Apakah halaman ini membantu?
0 / 5 - 0 peringkat