Skip to content

Drag and drop

Use in the DropdownMenu

tsx
//https://github.com/semrush/intergalactic/tree/master/website/docs/components/drag-and-drop/examples/list.tsx
import React from 'react';
import Select from '@semcore/ui/select';
import DnD from '@semcore/ui/drag-and-drop';

const initialOptions = Array(6)
  .fill(0)
  .map((i, idx) => ({
    value: idx,
    title: `Awesome option ${idx}`,
  }));

const Demo = () => {
  const [options, setOptions] = React.useState(initialOptions);

  const handleDnD = React.useCallback(
    ({ fromIndex, toIndex }: { fromIndex: number; toIndex: number }) => {
      setOptions((options) => {
        const newOptions = [...options];
        const swap = newOptions[fromIndex];
        newOptions[fromIndex] = newOptions[toIndex];
        newOptions[toIndex] = swap;
        return newOptions;
      });
    },
    [options],
  );

  return (
    <Select multiselect>
      <Select.Trigger />
      <Select.Menu>
        {({ highlightedIndex }) => {
          return (
            <DnD onDnD={handleDnD} customFocus={highlightedIndex}>
              {options.map((option, idx) => {
                const { value, title } = option;
                return (
                  <DnD.Draggable tag={Select.Option} value={value} key={idx} pr={5}>
                    {title}
                  </DnD.Draggable>
                );
              })}
            </DnD>
          );
        }}
      </Select.Menu>
    </Select>
  );
};
//https://github.com/semrush/intergalactic/tree/master/website/docs/components/drag-and-drop/examples/list.tsx
import React from 'react';
import Select from '@semcore/ui/select';
import DnD from '@semcore/ui/drag-and-drop';

const initialOptions = Array(6)
  .fill(0)
  .map((i, idx) => ({
    value: idx,
    title: `Awesome option ${idx}`,
  }));

const Demo = () => {
  const [options, setOptions] = React.useState(initialOptions);

  const handleDnD = React.useCallback(
    ({ fromIndex, toIndex }: { fromIndex: number; toIndex: number }) => {
      setOptions((options) => {
        const newOptions = [...options];
        const swap = newOptions[fromIndex];
        newOptions[fromIndex] = newOptions[toIndex];
        newOptions[toIndex] = swap;
        return newOptions;
      });
    },
    [options],
  );

  return (
    <Select multiselect>
      <Select.Trigger />
      <Select.Menu>
        {({ highlightedIndex }) => {
          return (
            <DnD onDnD={handleDnD} customFocus={highlightedIndex}>
              {options.map((option, idx) => {
                const { value, title } = option;
                return (
                  <DnD.Draggable tag={Select.Option} value={value} key={idx} pr={5}>
                    {title}
                  </DnD.Draggable>
                );
              })}
            </DnD>
          );
        }}
      </Select.Menu>
    </Select>
  );
};

Use in TabPanel

tsx
import React from 'react';
import DnD from '@semcore/ui/drag-and-drop';
import Badge from '@semcore/ui/badge';
import LinkedInM from '@semcore/ui/icon/LinkedIn/m';
import TabPanel from '@semcore/ui/tab-panel';

const icons = {
  social: (
    <TabPanel.Item.Addon>
      <LinkedInM />
    </TabPanel.Item.Addon>
  ),
  issues: (
    <TabPanel.Item.Addon>
      <Badge bg='red'>new</Badge>
    </TabPanel.Item.Addon>
  ),
};
const titles = {
  overview: 'Overview',
  issues: 'Issues',
  social: 'LinkedIn',
};

const Demo = () => {
  const [tabs, setTabs] = React.useState(['overview', 'issues', 'social']);
  const [currentTab, setCurrentTab] = React.useState('overview');
  const handleDnD = React.useCallback(({ fromIndex, toIndex }) => {
    setTabs((tabs) => {
      const from = tabs[fromIndex];
      tabs[fromIndex] = tabs[toIndex];
      tabs[toIndex] = from;
      return [...tabs];
    });
  }, []);

  return (
    <DnD
      tag={TabPanel}
      value={currentTab}
      onChange={(tab) => setCurrentTab(tab as string)}
      onDnD={handleDnD}
    >
      {tabs.map((tab) => (
        <DnD.Draggable placement='bottom' tag={TabPanel.Item} value={tab} key={tab} pb={0}>
          {icons[tab] ?? null}
          <TabPanel.Item.Text>{titles[tab]}</TabPanel.Item.Text>
        </DnD.Draggable>
      ))}
    </DnD>
  );
};
import React from 'react';
import DnD from '@semcore/ui/drag-and-drop';
import Badge from '@semcore/ui/badge';
import LinkedInM from '@semcore/ui/icon/LinkedIn/m';
import TabPanel from '@semcore/ui/tab-panel';

const icons = {
  social: (
    <TabPanel.Item.Addon>
      <LinkedInM />
    </TabPanel.Item.Addon>
  ),
  issues: (
    <TabPanel.Item.Addon>
      <Badge bg='red'>new</Badge>
    </TabPanel.Item.Addon>
  ),
};
const titles = {
  overview: 'Overview',
  issues: 'Issues',
  social: 'LinkedIn',
};

const Demo = () => {
  const [tabs, setTabs] = React.useState(['overview', 'issues', 'social']);
  const [currentTab, setCurrentTab] = React.useState('overview');
  const handleDnD = React.useCallback(({ fromIndex, toIndex }) => {
    setTabs((tabs) => {
      const from = tabs[fromIndex];
      tabs[fromIndex] = tabs[toIndex];
      tabs[toIndex] = from;
      return [...tabs];
    });
  }, []);

  return (
    <DnD
      tag={TabPanel}
      value={currentTab}
      onChange={(tab) => setCurrentTab(tab as string)}
      onDnD={handleDnD}
    >
      {tabs.map((tab) => (
        <DnD.Draggable placement='bottom' tag={TabPanel.Item} value={tab} key={tab} pb={0}>
          {icons[tab] ?? null}
          <TabPanel.Item.Text>{titles[tab]}</TabPanel.Item.Text>
        </DnD.Draggable>
      ))}
    </DnD>
  );
};

Example with use of the drop zone

tsx
import React from 'react';
import DnD from '@semcore/ui/drag-and-drop';
import Card from '@semcore/ui/card';
import { Row, Col } from '@semcore/ui/grid';

const titles = { backlink: 'Backlink', keyword: 'Keyword', seo: 'On Page SEO' };
const Demo = () => {
  const [items, setItems] = React.useState(['backlink', 'keyword', 'seo']);
  const [saved, setSaved] = React.useState({});
  const handleDnD = React.useCallback(({ fromId, toId }) => {
    if (toId === 'drop-zone') {
      setSaved((saved) => ({ ...saved, [fromId]: true }));
    } else {
      setItems((items) => {
        const newItems = [...items];
        const fromIndex = items.indexOf(fromId);
        const toIndex = items.indexOf(toId);
        newItems[fromIndex] = items[toIndex];
        newItems[toIndex] = items[fromIndex];
        return newItems;
      });
    }
  }, []);

  return (
    <DnD tag={Row} gutter={4} onDnD={handleDnD}>
      <Col span={12} mb={4}>
        <DnD.DropZone h={73} style={{ display: 'flex' }} id='drop-zone'>
          {items
            .filter((item) => saved[item])
            .map((item) => (
              <Card key={item} mr={4}>
                <Card.Title tag='h4' inline my={0}>
                  {titles[item]}
                </Card.Title>
              </Card>
            ))}
        </DnD.DropZone>
      </Col>

      {items
        .filter((item) => !saved[item])
        .map((item) => (
          <Col span={4} mb={4} key={item}>
            <DnD.Draggable placement='top' id={item}>
              <Card>
                <Card.Title tag='h4' inline my={0}>
                  {titles[item]}
                </Card.Title>
              </Card>
            </DnD.Draggable>
          </Col>
        ))}
    </DnD>
  );
};
import React from 'react';
import DnD from '@semcore/ui/drag-and-drop';
import Card from '@semcore/ui/card';
import { Row, Col } from '@semcore/ui/grid';

const titles = { backlink: 'Backlink', keyword: 'Keyword', seo: 'On Page SEO' };
const Demo = () => {
  const [items, setItems] = React.useState(['backlink', 'keyword', 'seo']);
  const [saved, setSaved] = React.useState({});
  const handleDnD = React.useCallback(({ fromId, toId }) => {
    if (toId === 'drop-zone') {
      setSaved((saved) => ({ ...saved, [fromId]: true }));
    } else {
      setItems((items) => {
        const newItems = [...items];
        const fromIndex = items.indexOf(fromId);
        const toIndex = items.indexOf(toId);
        newItems[fromIndex] = items[toIndex];
        newItems[toIndex] = items[fromIndex];
        return newItems;
      });
    }
  }, []);

  return (
    <DnD tag={Row} gutter={4} onDnD={handleDnD}>
      <Col span={12} mb={4}>
        <DnD.DropZone h={73} style={{ display: 'flex' }} id='drop-zone'>
          {items
            .filter((item) => saved[item])
            .map((item) => (
              <Card key={item} mr={4}>
                <Card.Title tag='h4' inline my={0}>
                  {titles[item]}
                </Card.Title>
              </Card>
            ))}
        </DnD.DropZone>
      </Col>

      {items
        .filter((item) => !saved[item])
        .map((item) => (
          <Col span={4} mb={4} key={item}>
            <DnD.Draggable placement='top' id={item}>
              <Card>
                <Card.Title tag='h4' inline my={0}>
                  {titles[item]}
                </Card.Title>
              </Card>
            </DnD.Draggable>
          </Col>
        ))}
    </DnD>
  );
};

Released under the MIT License.

Released under the MIT License.