SortableJS / react-sortablejs

React bindings for SortableJS
http://sortablejs.github.io/react-sortablejs/
MIT License
2.07k stars 211 forks source link

How do I render text inside nested children component? #288

Open MaximGordiyenko opened 1 year ago

MaximGordiyenko commented 1 year ago

I’m stuck with dragging text inside specific block that already is ReactSortable nested component. I did not find solution to render content inside nested ReactSortable component. What I expect:

Dragging layout block ( example: 2 blocks ) into editor. Dragging paragraph or heading into specific block (ex: in the first paragraph, in the second heading).

What I get:

  1. Dragging layout block appears in Editor.
  2. Dragging paragraph or heading doesn’t appear into specific block.

This source code:

import { useState, createContext, useContext } from 'react';
import { ReactSortable } from 'react-sortablejs';

export const MyContext = createContext(null);

const orthographyInitState = [
  { id: 'p1', type: 'paragraph', content: <p>Paragraph</p> },
  { id: 'h1', type: 'heading', content: <h3>Heading</h3> },
];

const initialLayout2 = Array.from({ length: 2 }, (_, index) => ({
  id: `l-2-${index + 1}`,
  context: [],
  width: ['30%', '70%'],
}));
const initialLayout6 = Array.from({ length: 6 }, (_, index) => ({
  id: `l-2-${index + 1}`,
  context: [],
}));

const Layout2 = () => {
  const { editorState, setEditorState, layoutState, setLayoutState } = useContext(MyContext);

  return (
    <div style={{ display: 'flex' }}>
      {initialLayout2?.map((layoutItem, idx) => (
        <ReactSortable
          key={layoutItem.id}
          group={{
            name: 'layout-item',
            pull: true,
            put: ['orthography-list', 'editor-list'],
          }}
          animation={150}
          list={layoutItem.context}
          setList={(currentChild) => {}}
          style={{
            border: '1px solid blue',
            margin: 10,
            minHeight: 50,
            width: layoutItem?.width[idx],
          }}
          className="layout-container">
          {layoutItem.context?.map((item, idx) => (
            <div key={`${item.id}`} data-id={`${item.id}`} className="layout-content">
              {item.content}
            </div>
          ))}
        </ReactSortable>
      ))}
    </div>
  );
};
const Layout6 = () => {
  const initialLayout2 = Array.from({ length: 6 }, (_, index) => ({
    id: `layout-6-${index + 1}`,
    content: [],
  }));
  const [layoutState, setLayoutState] = useState(initialLayout2);

  return (
    <ReactSortable
      list={layoutState}
      setList={(newList) => setLayoutState(newList)}
      group={{
        name: 'layout-6',
        pull: true,
        put: [],
      }}
      animation={150}
      style={{
        border: '1px solid skyblue',
        margin: 10,
        minHeight: 50,
        minWidth: 50,
        display: 'flex',
        flexDirection: 'row',
      }}
      className="layout-block">
      {layoutState.map((sortable) => (
        <ReactSortable
          key={sortable.id}
          data-id={sortable.id}
          group={{
            name: 'layout-item',
            pull: true,
            put: ['orthography-list', 'editor-list'],
          }}
          animation={150}
          list={sortable.content}
          setList={(newList) => {
            const updatedState = layoutState.map((item) =>
              item.id === sortable.id ? { ...item, content: [...item.content, ...newList] } : item,
            );
            console.log(`%c newList`, `color: #0af015`, newList);
            setLayoutState(updatedState);
          }}
          style={{
            border: '1px solid blue',
            margin: 10,
            minHeight: 50,
            minWidth: 50,
          }}
          className="layout-container">
          {sortable.content.map((item, idx) => {
            console.log(`%c item`, `color: #0a9df0`, item);
            return (
              <div
                key={`${item.id}-${idx}`}
                data-id={`${item.id}-${idx}`}
                className="layout-content">
                {item.content}
              </div>
            );
          })}
        </ReactSortable>
      ))}
    </ReactSortable>
  );
};

const layoutInitialState = [
  {
    type: `layout-2`,
    group: 'layout',
    icon: '◼︎◼︎',
    content: <Layout2 />,
    child: initialLayout2,
  },
  {
    type: 'layout-6',
    group: 'layout',
    icon: '◼︎◼︎◼︎◼︎◼︎◼︎',
    content: <Layout6 />,
    child: initialLayout6,
  },
];

export const App = () => {
  const [orthographyState, setOrthographyState] = useState(orthographyInitState);
  const [layoutState, setLayoutState] = useState(layoutInitialState);
  const [editorState, setEditorState] = useState([]);
  const values = { orthographyState, layoutState, setLayoutState, editorState, setEditorState };

  return (
    <MyContext.Provider value={values}>
      <div style={{ display: 'flex', flexDirection: 'row' }}>
        <div style={{ display: 'flex', flexDirection: 'column' }}>
          <Orthography />
          <Layout />
        </div>
        <Editor />
      </div>
    </MyContext.Provider>
  );
};

const Orthography = () => {
  const { orthographyState } = useContext(MyContext);
  return (
    <ReactSortable
      list={orthographyState}
      setList={() => {}}
      group={{
        name: 'orthography-list',
        pull: 'clone',
        put: [],
      }}
      sort={false}
      animation={150}
      style={{ border: '1px solid red' }}
      className="orthography-container">
      {orthographyState.map((item, idx) => (
        <div
          key={`${item.id}-${idx}`}
          data-id={`${item.id}-${idx}`}
          className={`orthography-content`}>
          {item.content}
        </div>
      ))}
    </ReactSortable>
  );
};

const Layout = () => {
  const { layoutState } = useContext(MyContext);
  return (
    <ReactSortable
      list={layoutState}
      setList={() => {}}
      group={{
        name: 'layout-list',
        pull: 'clone',
        put: [],
      }}
      sort={false}
      animation={150}
      style={{ border: '1px solid red' }}
      className="layout-container">
      {layoutState.map((item, idx) => (
        <div key={`${item.id}-${idx}`} data-id={`${item.id}-${idx}`} className={`layout-content`}>
          {item.icon}
        </div>
      ))}
    </ReactSortable>
  );
};

const Editor = () => {
  const { editorState, setEditorState, layoutState, setLayoutState } = useContext(MyContext);

  return (
    <ReactSortable
      list={editorState}
      setList={(currentState) => {
        const updatedState = currentState.map((item, idx) => ({
          ...item,
          id: `${item.type}-${idx}`,
        }));
        setEditorState(updatedState);
      }}
      group={{
        name: 'editor-list',
        pull: true,
        put: ['layout-list', 'orthography-list', 'layout-item'],
      }}
      animation={150}
      style={{ border: '1px solid green', minHeight: 200, minWidth: 600, margin: 10 }}
      className="editor-container">
      {editorState?.map((item, idx) => (
        <div key={`${item.id}-${idx}`} data-id={`${item.id}-${idx}`} className="editor-content">
          {item.content}
        </div>
      ))}
    </ReactSortable>
  );
};