如何使可拖放的 div 也可编辑?

问题描述 投票:0回答:2

我正在使用 dnd-kit/core 并且无法通过编辑按钮使我的 UserComponent 可编辑。我不认为我写的编辑按钮可能与 dnd-kit 兼容(因为它不起作用)?任何提示或解决方案将不胜感激!!

谢谢。

import React, { useState, useEffect } from 'react';
import { closestCenter, DndContext, PointerSensor, useSensor } from '@dnd-kit/core';
import { arrayMove, SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';



const UserComponent = ({
  id,
  body
}) => {
  const [isEditing, setIsEditing] = useState(false);

  const toggleEditing = () => {
    setIsEditing(!isEditing);
  };

  const handleBlur = () `your text`=> {
    setIsEditing(false);
  };

  const {
      setNodeRef,
      attributes,
      listeners,
      transition,
      transform,
      isDragging,
  } = useSortable({ id: id })

  const style = {
      transition,
      transform: CSS.Transform.toString(transform),
      border: '2px solid black',
      marginBottom: 5,
      marginTop: 5,
      display: "block",
      opacity: isDragging ? 0.5 : 1,
  }

  return (
      <>
      <div
          ref={setNodeRef}
          {...attributes}
          {...listeners}
          style={style}
      >
        <button onClick={toggleEditing}>Edit</button>
        <div
          contentEditable={isEditing}
          onBlur={handleBlur}
          suppressContentEditableWarning
        >
          {body}
        </div>
      </div>
      
     </>
  )
}

function DragApp() {

  const [items, setItems] = useState([
      {
        id: "1",
        name: "Manoj"
      },
      {
        id: "2",
        name: "John"
      },
      {
        id: "3",
        name: "Ronaldo"
      },
      {
        id: "4",
        name: "Harry"
      },
      {
        id: "5",
        name: "Jamie"
      }
    ])

  useEffect(() => {
      fetch('https://jsonplaceholder.typicode.com/posts')
        .then((response) => response.json())
        .then((data) => setItems(data));
    }, []);

const sensors = [useSensor(PointerSensor)];

const handleDragEnd = ({active, over}) => {
  if (active.id !== over.id) {
    setItems((items) => {
      const oldIndex = items.findIndex(item => item.id === active.id)
      const newIndex = items.findIndex(item => item.id === over.id)

      return arrayMove(items, oldIndex, newIndex)
    })
  }
}

return (
  <div
    style={{
      margin: 'auto',
      width: 1000,
      textAlign: 'center',

    }}
  >
    <DndContext
      sensors={sensors}
      collisionDetection={closestCenter}
      onDragEnd={handleDragEnd}
    >
      <SortableContext
        items={items.map(item => item.id)}
        strategy={verticalListSortingStrategy}
      >
        {
          items.map(
            item => <UserComponent {...item} key={item.id} />
          )
        }
      </SortableContext>
    </DndContext>
  </div>
);
}

export default DragApp;

我尝试了上面的代码,并希望编辑按钮使每个 div 都可编辑——每个编辑按钮都特定于 div。

实际的结果是文本通过了,我可以拖放div,但是div不可编辑,编辑按钮不可点击。

reactjs contenteditable dnd-kit
2个回答
0
投票

你应该添加两件事:

  1. 在 isEditing 状态添加“disabled: true”以使用 Sortable 道具(当您编辑文本时它将停止拖动):
  const {
    setNodeRef,
    attributes,
    listeners,
    transition,
    transform,
    isDragging,
  } = useSortable({ id: id, disabled: isEditing && true });
  1. 覆盖传感器的激活器功能,制作新的:
class MyPointerSensor extends PointerSensor {
  static activators = [
    {
      eventName: "onPointerDown",
      handler: ({ nativeEvent: event }) => {
        if (
          !event.isPrimary ||
          event.button !== 0 ||
          isInteractiveElement(event.target)
        ) {
          return false;
        }

        return true;
      },
    },
  ];
}

function isInteractiveElement(element) {
  const interactiveElements = [
    "button",
    "input",
    "textarea",
    "select",
    "option",
  ];

  if (interactiveElements.includes(element.tagName.toLowerCase())) {
    return true;
  }

  return false;
}

它将使按钮、输入和文本区域成为不可拖动的元素。

然后更换传感器:

  const sensors = [useSensor(MyPointerSensor)];

您可以在这里获得更多信息:How do I prevent draggable on input and btns


0
投票

我不得不做一些修改并添加 TypeScript 支持。我正在使用

MuiChip
里面的
DndContext
。 PointerSensor 正在拦截我的 onDelete 指针事件。

我的 React 代码的 HTML 表示形式

<div class="MuiButtonBase-root MuiChip-root MuiChip-filled MuiChip-sizeMedium MuiChip-colorDefault MuiChip-deletable MuiChip-deletableColorDefault MuiChip-filledDefault __className_98cea1 mui-style-11eqgit-MuiButtonBase-root-MuiChip-root-SortableDnDChip" tabindex="0" role="button">
  <span class="MuiChip-label MuiChip-labelMedium mui-style-6od3lo-MuiChip- 
    label">
    {text}
  </span>
  <svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium MuiChip-deleteIcon 
    MuiChip-deleteIconMedium MuiChip-deleteIconColorDefault MuiChip- 
    deleteIconFilledColorDefault mui-style-1vgma3c-MuiSvgIcon-root" 
    focusable="false" aria-hidden="true" viewBox="0 0 24 24" data- 
    testid="CancelIcon">
    <path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 
      2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 
      10.59 15.59 7 17 8.41 13.41 12 17 15.59z">
    </path>
  </svg>
</div>

import { PointerEvent } from 'react';

import { PointerSensor, PointerSensorOptions } from '@dnd-kit/core';

const parentHasClass = (element: HTMLElement | null, className: string): boolean => {
  if (!element || !element.parentElement) {
    return false;
  }
  return element.parentElement.classList.contains(className);
};

const checkParentClass = (event: Event): boolean => {
  const target = event.target as HTMLElement;
  return parentHasClass(target, 'MuiChip-deleteIcon');
};

class RestrictedPointerSensor extends PointerSensor {
  static activators = [
    {
      eventName: 'onPointerDown' as const,
      handler: ({ nativeEvent: event }: PointerEvent, { onActivation }: PointerSensorOptions) => {
        if (checkParentClass(event)) {
          return false;
        }
        onActivation?.({ event });
        return true;
      },
    },
  ];
}

export default RestrictedPointerSensor;

  const sensors = useSensors(
    useSensor(RestrictedPointerSensor),
    useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
  );
© www.soinside.com 2019 - 2024. All rights reserved.