refactor: drag and drop

This commit is contained in:
chenos 2021-08-20 22:30:29 +08:00
parent 7d604cdf9a
commit 2e8fe58246
22 changed files with 506 additions and 361 deletions

View File

@ -43,12 +43,12 @@ export function DragHandle(props) {
const Icon = component || DragOutlined;
return (
<SortableItemContext.Consumer>
{({ setDraggableNodeRef, attributes, listeners }) =>
{({ draggable, setDraggableNodeRef, attributes, listeners }) =>
setDraggableNodeRef && (
<Icon
ref={setDraggableNodeRef}
{...others}
{...attributes}
// {...attributes}
{...listeners}
/>
)
@ -57,7 +57,14 @@ export function DragHandle(props) {
);
}
export function Droppable(props) {
export interface DroppableProps {
id: any;
data?: any;
component?: any;
[key: string]: any;
}
export function Droppable(props: DroppableProps) {
const { id, data = {}, className, component, children, ...others } = props;
const { isOver, setNodeRef: setDroppableNodeRef } = useDroppable({
id: `droppable-${id}`,
@ -81,8 +88,24 @@ export function Droppable(props) {
);
}
export function SortableItem(props) {
const { id, data = {}, className, component, children, ...others } = props;
export interface SortableItemProps {
id: any;
data?: any;
component?: any;
draggable?: boolean;
[key: string]: any;
}
export function SortableItem(props: SortableItemProps) {
const {
id,
data = {},
draggable,
className,
component,
children,
...others
} = props;
const previewRef = useRef<HTMLElement>();
const { isOver, setNodeRef: setDroppableNodeRef } = useDroppable({
id: `droppable-${id}`,
@ -104,10 +127,14 @@ export function SortableItem(props) {
previewRef,
},
});
if (draggable) {
Object.assign(others, listeners);
}
const Component = component || Div;
return (
<Component
{...others}
{...attributes}
className={cls(className, `droppable-${id}`, {
isOver,
isDragging,
@ -115,10 +142,13 @@ export function SortableItem(props) {
ref={(el: HTMLElement) => {
previewRef.current = el;
setDroppableNodeRef(el);
if (draggable) {
setDraggableNodeRef(el);
}
}}
>
<SortableItemContext.Provider
value={{ setDraggableNodeRef, attributes, listeners }}
value={{ draggable, setDraggableNodeRef, attributes, listeners }}
>
{children}
</SortableItemContext.Provider>

View File

@ -446,12 +446,24 @@ function generateCardItemSchema(component) {
name: uid(),
'x-decorator': 'Form',
'x-component': 'Kanban.Card',
'x-designable-bar': 'Kanban.Card.DesignableBar',
'x-read-pretty': true,
'x-decorator-props': {
useResource: '{{ Kanban.useResource }}',
},
properties: {},
properties: {
[uid()]: {
type: 'void',
'x-decorator': 'BlockItem',
'x-decorator-props': {
draggable: false,
},
'x-component': 'Grid',
'x-designable-bar': 'Kanban.Card.DesignableBar',
// 'x-component-props': {
// addNewComponent: 'AddNew.FormItem',
// },
},
},
},
view1: {
type: 'void',

View File

@ -14,7 +14,6 @@ import classNames from 'classnames';
import { MenuOutlined, DragOutlined } from '@ant-design/icons';
import './style.less';
import get from 'lodash/get';
import { GridBlockContext } from '../grid';
import { uid } from '@formily/shared';
import { useDesignable, useSchemaPath } from '../';
import { AddNew } from '../add-new';
@ -26,89 +25,45 @@ import {
useDragDropUID,
} from '../../components/drag-and-drop';
import cls from 'classnames';
import { Droppable, SortableItem } from '../../components/Sortable';
import { useDndContext } from '@dnd-kit/core';
import { getSchemaPath } from '../../components/schema-renderer';
const DraggableBlock = (props) => {
const { children, ...others } = props;
const { DesignableBar } = useDesignable();
const { isDragging, dragRef, previewRef, isOver, onTopHalf, dropRef } =
useBlockDragAndDrop();
const schema = useFieldSchema();
const [active, setActive] = useState(false);
const { className, children, ...others } = props;
const { DesignableBar, schema } = useDesignable();
return (
<DraggableBlockContext.Provider value={{ dragRef }}>
<div
// onMouseEnter={(e) => {
// setActive(true);
// // console.log('e.onMouseEnter', new Date().toString());
// }}
// onMouseMove={(event) => {
// let dropElement = document.elementFromPoint(
// event.clientX,
// event.clientY,
// );
// const dropIds = [];
// while (dropElement) {
// if (!dropElement.getAttribute) {
// dropElement = dropElement.parentNode as HTMLElement;
// continue;
// }
// const dropId = dropElement.getAttribute('data-drop-id');
// if (dropId) {
// dropIds.push(dropId);
// }
// // if (dropId && dropId !== schema.name) {
// // setActive(false);
// // break;
// // }
// dropElement = dropElement.parentNode as HTMLElement;
// }
// if (dropIds.length > 0) {
// setActive(dropIds[0] === schema.name);
// }
// // console.log('e.onMouseMove', dropIds, schema.name);
// }}
// onMouseLeave={(e) => {
// setActive(false);
// // console.log('e.onMouseLeave', new Date().toString());
// }}
ref={mergeRefs([previewRef, dropRef])}
className={cls('nb-grid-block', 'designable-form-item', {
'top-half': onTopHalf,
hover: isOver,
active,
dragging: isDragging,
})}
style={{ marginBottom: 24 }}
<SortableItem
id={schema.name}
className={cls('nb-block-item', className)}
data={{
type: 'block',
path: getSchemaPath(schema),
}}
{...others}
>
{children}
<DesignableBar />
</div>
</DraggableBlockContext.Provider>
</SortableItem>
);
};
const Block = (props) => {
const { DesignableBar } = useDesignable();
const [active, setActive] = useState(false);
const { className, children, ...others } = props;
return (
<div
onMouseEnter={(e) => {
setActive(true);
// console.log('e.onMouseEnter', new Date().toString());
}}
onMouseLeave={(e) => {
setActive(false);
// console.log('e.onMouseLeave', new Date().toString());
}}
className={cls('nb-grid-block', 'designable-form-item', { active })}
>
{props.children}
<div className={cls('nb-block-item', className)} {...others}>
{children}
<DesignableBar />
</div>
);
};
export const BlockItem: any = observer((props) => {
const uid = useDragDropUID();
return React.createElement(uid ? DraggableBlock : Block, props);
export const BlockItem: any = observer((props: any) => {
const { draggable = true } = props;
const ctx = useDndContext();
return React.createElement(
draggable && ctx.activators?.length > 0 ? DraggableBlock : Block,
props,
);
});

View File

@ -1,4 +1,4 @@
.designable-form-item {
.nb-block-item {
position: relative;
&:hover,
&.active {
@ -45,4 +45,7 @@
right: -5px;
bottom: -5px;
}
&.nb-card-item {
margin-bottom: 24px;
}
}

View File

@ -36,6 +36,7 @@ import { useCollection, useCollectionContext } from '../../constate';
import { useTable } from '../table';
import { fieldsToFilterColumns } from './';
import { set } from 'lodash';
import { DragHandle } from '../../components/Sortable';
export const DesignableBar = observer((props) => {
const field = useField();
@ -60,7 +61,7 @@ export const DesignableBar = observer((props) => {
>
<Space size={2}>
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}

View File

@ -14,7 +14,6 @@ import classNames from 'classnames';
import { MenuOutlined, DragOutlined } from '@ant-design/icons';
// import './style.less';
import get from 'lodash/get';
import { GridBlockContext } from '../grid';
import { uid } from '@formily/shared';
import { useDesignable } from '../';
import { AddNew } from '../add-new';
@ -26,7 +25,7 @@ export const CardItem: any = connect((props) => {
const { schema } = useDesignable();
return (
<BlockSchemaContext.Provider value={schema}>
<BlockItem>
<BlockItem className={'nb-card-item'}>
<Card bordered={false} {...props}>
{props.children}
</Card>

View File

@ -20,6 +20,7 @@ import { removeSchema, updateSchema } from '..';
import { isGridRowOrCol } from '../grid';
import G2Plot from './G2Plot';
import { Column, Line, Pie, Bar } from '@antv/g2plot';
import { DragHandle } from '../../components/Sortable';
export const Chart: any = {};
@ -57,7 +58,7 @@ Chart.DesignableBar = observer((props) => {
>
<Space size={'small'}>
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}

View File

@ -15,6 +15,7 @@
}
.designable-bar {
z-index: 50 !important;
border-radius: 2px !important;
// border: 2px solid @bgcolor !important;
border: 0 !important;

View File

@ -14,12 +14,12 @@ import classNames from 'classnames';
import { MenuOutlined, DragOutlined } from '@ant-design/icons';
// import './style.less';
import get from 'lodash/get';
import { GridBlockContext } from '../grid';
import { uid } from '@formily/shared';
import { useDesignable } from '../';
import { AddNew } from '../add-new';
import { BlockItem } from '../block-item';
import { DraggableBlockContext } from '../../components/drag-and-drop';
import { DragHandle } from '../../components/Sortable';
export const FormItem: any = connect((props) => {
return (
@ -44,7 +44,7 @@ FormItem.DesignableBar = () => {
>
<Space size={'small'}>
<AddNew.FormItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}

View File

@ -29,6 +29,7 @@ import { getSchemaPath } from '../../components/schema-renderer';
import { useCollection, useCollectionContext } from '../../constate';
import { useTable } from '../table';
import { BlockSchemaContext } from '../../context';
import { DragHandle } from '../../components/Sortable';
export const DesignableBar = observer((props) => {
const field = useField();
@ -52,7 +53,7 @@ export const DesignableBar = observer((props) => {
>
<Space size={2}>
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}

View File

@ -35,6 +35,7 @@ import { getSchemaPath } from '../../components/schema-renderer';
import { RandomNameContext } from '.';
import { useCollectionContext, useDisplayedMapContext } from '../../constate';
import SwitchMenuItem from '../../components/SwitchMenuItem';
import { DragHandle } from '../../components/Sortable';
export const FieldDesignableBar = observer((props) => {
const field = useField();
@ -64,7 +65,7 @@ export const FieldDesignableBar = observer((props) => {
>
<Space size={2}>
<AddNew.FormItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<DragHandle />
<Dropdown
placement={'bottomRight'}
trigger={['click']}

View File

@ -2,13 +2,13 @@ import React from 'react';
import { uid } from '@formily/shared';
import {
observer,
ISchema,
FormProvider,
useFieldSchema,
RecursionField,
useField,
} from '@formily/react';
import { SchemaRenderer } from '../..';
import { ISchema, SchemaRenderer } from '../..';
import { Grid } from '..';
const schema: ISchema = {
type: 'void',
@ -104,13 +104,13 @@ const schema: ISchema = {
[`row_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Row',
'x-component-props': {
colsize: [30, 70],
},
properties: {
[`col_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Col',
'x-component-props': {
width: 30,
},
properties: {
[uid()]: {
type: 'string',
@ -124,9 +124,6 @@ const schema: ISchema = {
[`col_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Col',
'x-component-props': {
width: 70,
},
properties: {
[uid()]: {
type: 'string',
@ -190,6 +187,6 @@ const schema: ISchema = {
export default () => {
return (
<SchemaRenderer schema={schema} />
<SchemaRenderer components={{ Grid }} schema={schema} />
);
};

View File

@ -1,146 +1,166 @@
import React, {
FC,
CSSProperties,
useRef,
createContext,
useContext,
useEffect,
useState,
} from 'react';
// import { DndProvider, useDrag, useDragDropManager } from 'react-dnd';
// import { HTML5Backend } from 'react-dnd-html5-backend';
import {
DndContext,
DragOverlay,
useDndContext,
closestCorners,
rectIntersection,
closestCenter,
useSensors,
useSensor,
PointerSensor,
MouseSensor
} from '@dnd-kit/core';
import { observer, RecursionField } from '@formily/react';
import React, { useState } from 'react';
import { useContext } from 'react';
import { createContext } from 'react';
import {
findPropertyByPath,
getSchemaPath,
useDesignable,
useSchemaComponent,
} from '../../components/schema-renderer';
import { Droppable, SortableItem } from '../../components/Sortable';
import { uid } from '@formily/shared';
import {
observer,
ISchema,
FormProvider,
useFieldSchema,
RecursionField,
useField,
} from '@formily/react';
import './style.less';
import cls from 'classnames';
import { createSchema, removeSchema, useDesignable, useSchemaPath } from '../';
import './style.less';
import {
useDrag,
useDrop,
mergeRefs,
useColResizer,
useDragDropUID,
DragDropManagerProvider,
} from '../../components/drag-and-drop';
import { useSchemaComponent } from '../../components/schema-renderer';
createSchema,
FormilyISchema,
ISchema,
removeSchema,
updateSchema,
} from '..';
const ColumnSizeContext = createContext(null);
const GridRowContext = createContext<any>(null);
const GridColContext = createContext<any>(null);
export const GridBlockContext = createContext({
dragRef: null,
});
export function isGridRowOrCol(schema: ISchema) {
export function isGridRowOrCol(schema: ISchema | FormilyISchema) {
return ['Grid.Row', 'Grid.Col'].includes(schema['x-component']);
}
const RowDivider = ({ name, onDrop }) => {
const uid = useDragDropUID();
const { isOver, dropRef } = useDrop({
uid: `row_divider_${name}`,
accept: uid,
shallow: true,
onDrop,
});
return (
<div
ref={dropRef}
className={cls('nb-grid-row-divider', { hover: isOver })}
/>
);
};
const ColDivider = (props: any) => {
const { name, onDragEnd, resizable, onDrop } = props;
const uid = useDragDropUID();
const { isDragging, dragRef } = useColResizer({ onDragEnd });
const { isOver, dropRef } = useDrop({
uid: `col_divider_${name}`,
accept: uid,
shallow: true,
onDrop,
});
return (
<div
data-type={'col-divider'}
ref={mergeRefs([dragRef, dropRef])}
style={{ width: 24 }}
className={cls('nb-grid-col-divider', {
resizable,
hover: isOver,
dragging: isDragging,
})}
></div>
);
};
export const Grid: any = observer((props: any) => {
const { designable, root, schema, deepRemove, ...methods } = useDesignable();
const [dragOverlayContent, setDragOverlayContent] = useState('');
const [style, setStyle] = useState({});
const [active, setActive] = useState(false);
const [clientWidths, setClientWidths] = useState([0, 0]);
const { addNewComponent } = props;
const AddNewComponent = useSchemaComponent(addNewComponent);
const ref = useRef();
// const schema = useFieldSchema();
const gridPath = useSchemaPath();
const {
designable,
schema,
schema: designableSchema,
insertAfter,
prepend,
deepRemove,
} = useDesignable();
const sensors = useSensors(
useSensor(MouseSensor),
);
return (
<DragDropManagerProvider uid={schema.name}>
<div ref={ref} className={'nb-grid'}>
<RowDivider
key={`${schema.name}_0`}
name={`${schema.name}_0`}
onDrop={async (e) => {
const blockSchema = e.dragItem.schema;
const path = [...e.dragItem.path];
const data = prepend({
type: 'void',
'x-component': 'Grid.Row',
properties: {
[uid()]: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
[blockSchema.name]: blockSchema,
},
},
<div className={cls('nb-grid', { active })}>
<DndContext
autoScroll
sensors={sensors}
collisionDetection={rectIntersection}
onDragStart={(event) => {
setActive(true);
const el = event?.active?.data?.current?.previewRef
?.current as HTMLElement;
console.log(event, el);
setDragOverlayContent(el?.outerHTML);
setStyle({ width: el.clientWidth });
const activeType = event?.active?.data?.current?.type;
if (activeType === 'col-resize') {
const prev = el.previousElementSibling as HTMLDivElement;
const next = el.nextElementSibling as HTMLDivElement;
setClientWidths([prev.clientWidth, next.clientWidth]);
}
}}
onDragCancel={() => {
setActive(false);
}}
onDragMove={(event) => {
const activeType = event?.active?.data?.current?.type;
const el = event?.active?.data?.current?.previewRef
?.current as HTMLElement;
if (activeType === 'col-resize') {
const prev = el.previousElementSibling as HTMLDivElement;
const next = el.nextElementSibling as HTMLDivElement;
prev.style.width = `calc(${clientWidths[0]}px + ${event.delta.x}px)`;
next.style.width = `calc(${clientWidths[1]}px - ${event.delta.x}px)`;
return;
}
}}
onDragOver={(event) => {
const activeType = event?.active?.data?.current?.type;
console.log({ event });
if (activeType === 'col-resize') {
return;
}
}}
onDragEnd={async (event) => {
setActive(false);
const activeType = event?.active?.data?.current?.type;
const sourcePath = event?.active?.data?.current?.path;
const targetPath = event?.over?.data?.current?.path;
const method = event?.over?.data?.current?.method;
const type = event?.over?.data?.current?.type;
if (activeType === 'col-resize') {
const parentPath = event?.active?.data?.current?.parentPath;
const el = event?.active?.data?.current?.previewRef
?.current as HTMLElement;
const els = el.parentElement.querySelectorAll(
':scope > .nb-grid-col',
);
const size = [];
const gap = el.clientWidth;
els.forEach((el: HTMLDivElement) => {
// const w = (100 * el.clientWidth) / el.parentElement.clientWidth;
const w2 =
(100 * (el.clientWidth + gap + gap / els.length)) /
el.parentElement.clientWidth;
size.push(w2);
// el.style.width = `${w}%`;
});
const parent = findPropertyByPath(root, parentPath);
parent['x-component-props'] = parent['x-component-props'] || {};
parent['x-component-props']['colsize'] = size;
await updateSchema({
key: parent['key'],
'x-component-props': {
colsize: size,
},
});
await createSchema(data);
console.log('prepend', data);
const removed = deepRemove(path);
const last = removed.pop();
if (isGridRowOrCol(last)) {
await removeSchema(last);
return;
}
}}
/>
{schema.mapProperties((property, key, index) => {
return (
<>
<div style={{ display: 'flex' }} className={'nb-grid-row'}>
<RecursionField name={property.name} schema={property} />
</div>
<RowDivider
key={`${schema.name}_${index + 1}`}
name={`${schema.name}_${index + 1}`}
onDrop={async (e) => {
const blockSchema = e.dragItem.schema;
const path = [...e.dragItem.path];
const data = insertAfter(
{
if (!sourcePath || !targetPath) {
return;
}
if (sourcePath === targetPath) {
return;
}
let fn = methods[method];
if (!fn && type === 'block') {
fn = methods.insertAfter;
}
console.log({ event, sourcePath, targetPath, method, type });
if (!fn) {
return;
}
const sourceSchema = findPropertyByPath(root, sourcePath);
if (!sourceSchema) {
return;
}
if (!type) {
return;
}
let data;
if (['col-divider', 'col-resize'].includes(type)) {
data = {
type: 'void',
'x-component': 'Grid.Col',
properties: {
[sourceSchema.name]: sourceSchema.toJSON(),
},
};
} else if (['block', 'col'].includes(type)) {
data = sourceSchema.toJSON();
} else if (['row-divider', 'row'].includes(type)) {
data = {
type: 'void',
'x-component': 'Grid.Row',
properties: {
@ -148,131 +168,149 @@ export const Grid: any = observer((props: any) => {
type: 'void',
'x-component': 'Grid.Col',
properties: {
[blockSchema.name]: blockSchema,
[sourceSchema.name]: sourceSchema.toJSON(),
},
},
},
},
[...gridPath, key],
);
await createSchema(data);
const removed = deepRemove(path);
};
}
if (data) {
const removed = deepRemove(sourcePath);
const last = removed.pop();
if (isGridRowOrCol(last)) {
await removeSchema(last);
}
console.log('insertAfter', data, removed);
const s = fn(data, targetPath);
if (['block', 'col'].includes(type)) {
await updateSchema(s);
} else {
await createSchema(s);
}
}
}}
>
<DragOverlay
// style={{ pointerEvents: 'none' }}
>
<div
className={'nb-grid-drag-overlay'}
style={{
...style,
transform: 'translate(calc(-100% + 30px), 2px)',
}}
dangerouslySetInnerHTML={{ __html: dragOverlayContent }}
/>
</DragOverlay>
<Droppable
id={`${schema.name}-row-divider`}
className={'nb-grid-row-divider'}
data={{
type: 'row-divider',
method: 'prepend',
path: getSchemaPath(schema),
}}
/>
</>
);
{schema.mapProperties((property) => {
return <RecursionField name={property.name} schema={property} />;
})}
</DndContext>
{designable && AddNewComponent && <AddNewComponent />}
</div>
</DragDropManagerProvider>
);
});
Grid.Row = observer((props) => {
const field = useField();
// const schema = useFieldSchema();
const rowPath = useSchemaPath();
const {
schema,
schema: designableSchema,
refresh,
insertAfter,
appendChild,
prepend,
remove,
deepRemove,
} = useDesignable();
const len = Object.keys(designableSchema.properties || {}).length;
// console.log({ len, schema, designableSchema });
return (
<ColumnSizeContext.Provider value={len}>
<ColDivider
name={`${schema.name}_0`}
onDrop={async (e) => {
const blockSchema = e.dragItem.schema;
const data = prepend({
type: 'void',
'x-component': 'Grid.Col',
properties: {
[blockSchema.name]: blockSchema,
},
Grid.Row = observer((props: any) => {
const { schema } = useDesignable();
const columns = Object.values(schema.properties || {}).filter((item) => {
return !item['x-hidden'];
});
await createSchema(data);
const path = [...e.dragItem.path];
const removed = deepRemove(path);
const last = removed.pop();
if (isGridRowOrCol(last)) {
await removeSchema(last);
const columnCount = columns.length;
const path = getSchemaPath(schema);
let size = schema['x-component-props']?.['colsize'] || [];
if (size?.length !== columnCount) {
size = [];
}
return (
<GridRowContext.Provider value={{ columnCount }}>
<Droppable
id={schema.name}
className={'nb-grid-row'}
data={{
type: 'row',
method: 'appendChild',
path,
}}
>
<Droppable
id={`${schema.name}-first`}
className={'nb-grid-col-divider'}
data={{
type: 'col-divider',
method: 'prepend',
path: [...path],
}}
/>
{schema.mapProperties((property, key, index) => {
{columns.map((property, index) => {
return (
<>
<RecursionField name={property.name} schema={property} />
<ColDivider
name={`${schema.name}_${index + 1}`}
resizable={index < len - 1}
onDrop={async (e) => {
const blockSchema = e.dragItem.schema;
const data = insertAfter(
{
type: 'void',
'x-component': 'Grid.Col',
properties: {
[blockSchema.name]: blockSchema,
},
},
[...rowPath, key],
);
console.log('ColDivider', data);
await createSchema(data);
const path = [...e.dragItem.path];
const removed = deepRemove(path);
const last = removed.pop();
if (isGridRowOrCol(last)) {
await removeSchema(last);
}
}}
onDragEnd={(e) => {
designableSchema.mapProperties((s, key, index) => {
s['x-component-props'] = s['x-component-props'] || {};
s['x-component-props']['width'] = e.data.size[index];
return s;
});
schema.mapProperties((s, key, index) => {
s['x-component-props'] = s['x-component-props'] || {};
s['x-component-props']['width'] = e.data.size[index];
field.query(`.${schema.name}.${key}`).take((f) => {
f.componentProps['width'] = e.data.size[index];
});
return s;
});
// refresh();
{index > 0 && (
<SortableItem
draggable
id={`${schema.name}-${index}`}
data={{
type: 'col-resize',
method: 'insertBefore',
parentPath: [...path],
path: [...path, property.name],
}}
className={cls('nb-grid-col-divider', 'resizable')}
/>
)}
<GridColContext.Provider value={{ index, width: size[index] }}>
<RecursionField name={property.name} schema={property} />
</GridColContext.Provider>
</>
);
})}
</ColumnSizeContext.Provider>
<Droppable
id={`${schema.name}-last`}
className={'nb-grid-col-divider'}
data={{
type: 'col-divider',
method: 'appendChild',
path: [...path],
}}
/>
</Droppable>
</GridRowContext.Provider>
);
});
Grid.Col = observer((props) => {
const field = useField();
const width = field.componentProps['width'];
const size = useContext(ColumnSizeContext);
Grid.Col = observer((props: any) => {
const { schema } = useDesignable();
// const { width } = props;
const { columnCount } = useContext(GridRowContext);
const { width } = useContext(GridColContext);
return (
<div
style={{ width: `calc(${width || 100 / size}% - 24px - 24px / ${size})` }}
<Droppable
id={schema.name}
className={'nb-grid-col'}
style={{
width: `calc(${
width || 100 / columnCount
}% - 24px - 24px / ${columnCount})`,
}}
data={{
type: 'col',
method: 'appendChild',
path: getSchemaPath(schema),
}}
>
{/* <Droppable
id={`${schema.name}-divider`}
className={'nb-grid-row-divider'}
/> */}
{props.children}
</div>
</Droppable>
);
});

View File

@ -3,6 +3,7 @@
}
.nb-grid-row {
margin: 0px -24px;
display: flex;
}
// .nb-grid {
@ -96,3 +97,62 @@ body.dragging {
.anticon-drag {
cursor: grab;
}
.nb-block-item {
position: relative;
&.isDragging {
opacity: .3;
}
&.isOver::after {
content: '';
position: absolute;
bottom: -24px;
height: 20px;
width: 100%;
background: rgba(241, 139, 98, 0.1) !important;
}
}
.nb-grid-row,
.nb-grid-col {
position: relative;
&.isOver::after {
content: '';
position: absolute;
left: 0;
bottom: 0;
height: 20px;
width: 100%;
background: rgba(241, 139, 98, 0.1) !important;
}
}
.nb-grid-row-divider.isOver,
.nb-grid-col-divider.isOver {
background: rgba(241, 139, 98, 0.1) !important;
}
.nb-grid-drag-overlay {
opacity: 0.6;
.designable-bar {
background: rgba(241, 139, 98, 0.1) !important;
}
}
.nb-grid-row-divider {
// background-color: #ddd;
height: 24px;
width: 100%;
top: -24px;
position: absolute;
z-index: 20;
// opacity: 0.5;
// background: #e6f7ff;
}
.nb-grid-col-divider {
&.resizable {
cursor: col-resize;
&:hover {
background: rgba(241, 139, 98, 0.1) !important;
}
}
}

View File

@ -14,6 +14,7 @@ import { DraggableBlockContext } from '../../components/drag-and-drop';
import { uid } from '@formily/shared';
import { removeSchema, updateSchema } from '..';
import { isGridRowOrCol } from '../grid';
import { DragHandle } from '../../components/Sortable';
type ComposedInput = React.FC<InputProps> & {
TextArea?: React.FC<TextAreaProps>
@ -59,7 +60,7 @@ Input.DesignableBar = observer((props) => {
>
<Space size={'small'}>
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}

View File

@ -42,9 +42,8 @@ import SwitchMenuItem from '../../components/SwitchMenuItem';
export const CardDesignableBar = observer((props) => {
const field = useField();
const { designable, schema, appendChild, deepRemove } = useDesignable();
const { schema, appendChild, deepRemove } = useDesignable();
const [visible, setVisible] = useState(false);
const { dragRef } = useContext(DraggableBlockContext);
const { collection, fields } = useCollectionContext();
const displayed = useDisplayedMapContext();
console.log('useDisplayedMapContext', schema);

View File

@ -28,6 +28,7 @@ import { uid } from '@formily/shared';
import { getSchemaPath } from '../../components/schema-renderer';
import { useCollection, useCollectionContext } from '../../constate';
import { useTable } from '../table';
import { DragHandle } from '../../components/Sortable';
export const DesignableBar = observer((props) => {
const field = useField();
@ -48,7 +49,7 @@ export const DesignableBar = observer((props) => {
>
<Space size={2}>
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}

View File

@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useRef } from 'react';
import {
observer,
RecursionField,
@ -38,6 +38,7 @@ import { CardDesignableBar } from './CardDesignableBar';
import { VisibleContext } from '../../context';
export function SortableItem(props) {
const nodeRef = useRef<any>();
const {
attributes,
listeners,
@ -49,6 +50,7 @@ export function SortableItem(props) {
id: props.id,
data: {
type: props.type,
nodeRef,
},
});
@ -60,7 +62,10 @@ export function SortableItem(props) {
return (
<div
className={cls({ isDragging }, props.className)}
ref={setNodeRef}
ref={(el) => {
setNodeRef(el);
nodeRef.current = el;
}}
style={style}
{...attributes}
{...listeners}
@ -155,6 +160,10 @@ const InternalKanban = observer((props: any) => {
sort: 'sort',
});
}, [props.defaultFilter]);
const [dragOverlayContent, setDragOverlayContent] = useState('');
const [style, setStyle] = useState({});
const containerRef = useRef<HTMLDivElement>();
console.log({ style })
return (
<KanbanContext.Provider value={{ field, resource, service, props }}>
<div>
@ -162,8 +171,15 @@ const InternalKanban = observer((props: any) => {
<Button></Button>
</div> */}
<div className={'kanban'}>
<div className={'kanban-container'}>
<div ref={containerRef} className={'kanban-container'}>
<DndContext
onDragStart={(event) => {
const el = event?.active?.data?.current?.nodeRef
?.current as HTMLElement;
console.log(event, el);
setDragOverlayContent(el?.outerHTML);
setStyle({ width: el.clientWidth, height: containerRef.current?.clientHeight });
}}
// collisionDetection={closestCorners}
onDragEnd={({ active, over }) => {
const source = values.find((item) => item.id === active?.id);
@ -208,7 +224,17 @@ const InternalKanban = observer((props: any) => {
}
}}
>
<DragOverlay style={{ pointerEvents: 'none' }}>aaa</DragOverlay>
<DragOverlay
// style={{ pointerEvents: 'none' }}
>
<div
className={'nb-kanban-drag-overlay'}
style={{
...style,
}}
dangerouslySetInnerHTML={{ __html: dragOverlayContent }}
/>
</DragOverlay>
<SortableContext
strategy={horizontalListSortingStrategy}
@ -288,7 +314,9 @@ const InternalKanban = observer((props: any) => {
className={'column add-column'}
>
{/* <span className={'add-column-plus'}/> */}
<span><PlusOutlined/> </span>
<span>
<PlusOutlined />
</span>
</div>
</SortableContext>
</DndContext>

View File

@ -4,6 +4,9 @@
background: #f9f9f9;
margin-right: 12px;
padding: 12px;
&.isDragging {
opacity: .5;
}
&.add-column {
padding: 0;
margin-right: 0;
@ -35,6 +38,10 @@
}
}
.nb-kanban-drag-overlay {
opacity: .8;
}
.column-title {
line-height: 24px;
font-weight: 500;
@ -47,8 +54,7 @@
overflow: auto;
.item {
position: relative;
min-height: 50px;
background: #f1f1f1;
// background: #f1f1f1;
margin-bottom: 12px;
&.isDragging {
opacity: 0.5;
@ -68,6 +74,15 @@
.kanban .ant-card-body {
position: relative;
padding: 0;
> .nb-block-item {
padding: 12px;
min-height: 50px;
}
.nb-grid-block, .ant-formily-item {
margin-bottom: 0 !important;
}
&:hover {
> .designable-bar {
display: block;

View File

@ -21,6 +21,7 @@ import { uid } from '@formily/shared';
import { removeSchema, updateSchema } from '..';
import { isGridRowOrCol } from '../grid';
import './style.less';
import { DragHandle } from '../../components/Sortable';
export const Markdown: any = connect(
AntdInput.TextArea,
@ -121,7 +122,7 @@ Markdown.Void.DesignableBar = observer((props) => {
>
<Space size={'small'}>
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}
@ -182,7 +183,7 @@ Markdown.DesignableBar = observer((props) => {
>
<Space size={'small'}>
<AddNew.FormItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}

View File

@ -33,6 +33,7 @@ import { useEffect } from 'react';
import { uid } from '@formily/shared';
import { getSchemaPath } from '../../components/schema-renderer';
import { set } from 'lodash';
import { DragHandle } from '../../components/Sortable';
export const SimpleDesignableBar = observer((props) => {
const field = useField();
@ -54,7 +55,7 @@ export const SimpleDesignableBar = observer((props) => {
>
<Space size={'small'}>
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}

View File

@ -2015,7 +2015,7 @@ Table.DesignableBar = observer((props) => {
>
<Space size={2}>
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}