Skip to content

Commit

Permalink
Drag drop provider (#896)
Browse files Browse the repository at this point in the history
* add DragDrop example, resume drag in DragProvider

* full flow for remote drag

* fix type issues
  • Loading branch information
heswell authored Oct 8, 2023
1 parent 58287f0 commit f6374c1
Show file tree
Hide file tree
Showing 36 changed files with 910 additions and 683 deletions.
9 changes: 0 additions & 9 deletions vuu-ui/packages/vuu-layout/src/common-types.ts

This file was deleted.

16 changes: 5 additions & 11 deletions vuu-ui/packages/vuu-layout/src/drag-drop/BoxModel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ReactElement } from "react";
import { rect } from "../common-types";
import { boxContainsPoint } from "@finos/vuu-utils";
import { LayoutModel } from "../layout-reducer";
import { isContainer } from "../registry/ComponentRegistry";
import { getProps, typeOf } from "../utils";
Expand Down Expand Up @@ -132,7 +132,7 @@ export function getPosition(

if (targetOrientation === "row") {
position = pctX < 0.5 ? WEST : EAST;
} else if (rect.header && containsPoint(rect.header, x, y)) {
} else if (rect.header && boxContainsPoint(rect.header, x, y)) {
position = HEADER;

if (rect.Stack) {
Expand Down Expand Up @@ -199,7 +199,7 @@ function getPositionWithinBox(
pctY: number
) {
const centerBox = getCenteredBox(rect, 0.2);
if (containsPoint(centerBox, x, y)) {
if (boxContainsPoint(centerBox, x, y)) {
return CENTRE;
} else {
const quadrant = `${pctY < 0.5 ? "north" : "south"}${
Expand Down Expand Up @@ -474,7 +474,7 @@ function allBoxesContainingPoint(

const type = typeOf(component) as string;
const rect = measurements[path];
if (!containsPoint(rect, x, y)) return boxes;
if (!boxContainsPoint(rect, x, y)) return boxes;

if (dropTargets && dropTargets.length) {
if (dropTargets.includes(path)) {
Expand All @@ -494,7 +494,7 @@ function allBoxesContainingPoint(
return boxes;
}

if (rect.header && containsPoint(rect.header, x, y)) {
if (rect.header && boxContainsPoint(rect.header, x, y)) {
return boxes;
}

Expand All @@ -520,12 +520,6 @@ function allBoxesContainingPoint(
return boxes;
}

function containsPoint(rect: rect, x: number, y: number) {
if (rect) {
return x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom;
}
}

function scrollIntoViewIfNeccesary(
{ top, bottom, scrolling }: DragDropRect,
x: number,
Expand Down
2 changes: 1 addition & 1 deletion vuu-ui/packages/vuu-layout/src/drag-drop/Draggable.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { rect } from "@finos/vuu-utils";
import { ReactElement } from "react";
import { rect } from "../common-types";
import { LayoutModel } from "../layout-reducer";
import { findTarget, followPath, getProps } from "../utils";
import { BoxModel, Measurements, Position } from "./BoxModel";
Expand Down
2 changes: 1 addition & 1 deletion vuu-ui/packages/vuu-layout/src/drag-drop/DropTarget.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { rect, rectTuple } from "../common-types";
import { rect, rectTuple } from "@finos/vuu-utils";
import { LayoutModel } from "../layout-reducer";
import { getProps, typeOf } from "../utils";
import {
Expand Down
4 changes: 2 additions & 2 deletions vuu-ui/packages/vuu-layout/src/drag-drop/dragDropTypes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { rect } from '../common-types';
import type { rect } from "@finos/vuu-utils";
export interface DragDropRect extends rect {
children?: DragDropRect[];
header?: {
Expand Down Expand Up @@ -28,7 +28,7 @@ export interface DropPosition {
West: boolean;
}

export type RelativePosition = 'after' | 'before';
export type RelativePosition = "after" | "before";

export type DropPosTab = {
index: number;
Expand Down
1 change: 0 additions & 1 deletion vuu-ui/packages/vuu-layout/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from "./dock-layout";
export * from "./common-types";
export { default as Component } from "./Component";
export * from "./drag-drop";
export * from "./DraggableLayout";
Expand Down
17 changes: 10 additions & 7 deletions vuu-ui/packages/vuu-layout/src/layout-reducer/flexUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { uuid } from "@finos/vuu-utils";
import { dimension, rect, rectTuple, uuid } from "@finos/vuu-utils";
import React, { CSSProperties, ReactElement, ReactNode } from "react";
import { dimension, rect, rectTuple } from "../common-types";
import { DropPos } from "../drag-drop/dragDropTypes";
import { ComponentRegistry } from "../registry/ComponentRegistry";
import { getProps, resetPath } from "../utils";
Expand Down Expand Up @@ -91,13 +90,14 @@ export function getFlexStyle(
}

export function hasUnboundedFlexStyle(component: ReactElement) {
const { style: { flex, flexGrow, flexShrink, flexBasis } = NO_STYLE } = component.props;
const { style: { flex, flexGrow, flexShrink, flexBasis } = NO_STYLE } =
component.props;
if (typeof flex === "number") {
return true;
}
}
if (flexBasis === 0 && flexGrow === 1 && flexShrink === 1) {
return true;
}
}
if (typeof flexBasis === "number") {
return false;
}
Expand Down Expand Up @@ -212,11 +212,14 @@ export function wrapIntrinsicSizeComponentWithFlexbox(
);
}

const getFlexValue = (flexBasis: number, flexFill: boolean): number | undefined => {
const getFlexValue = (
flexBasis: number,
flexFill: boolean
): number | undefined => {
if (flexFill) {
return undefined;
}
return flexBasis === 0 ? 1 : 0
return flexBasis === 0 ? 1 : 0;
};

export function createFlexbox(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { uuid } from "@finos/vuu-utils";
import { rectTuple, uuid } from "@finos/vuu-utils";
import React, { ReactElement } from "react";
import { rectTuple } from "../common-types";
import { DropPos } from "../drag-drop";
import { DropTarget } from "../drag-drop/DropTarget";
import { getProp, getProps, nextStep, resetPath, typeOf } from "../utils";
Expand All @@ -11,7 +10,7 @@ import {
getFlexDimensions,
getFlexOrIntrinsicStyle,
getIntrinsicSize,
wrapIntrinsicSizeComponentWithFlexbox
wrapIntrinsicSizeComponentWithFlexbox,
} from "./flexUtils";
import { LayoutModel } from "./layoutTypes";
import { getManagedDimension, LayoutProps } from "./layoutUtils";
Expand Down Expand Up @@ -325,12 +324,14 @@ function getStyledComponents(
newComponent: ReactElement,
targetRect: DropTarget["clientRect"]
): [ReactElement, ReactElement] {
const id = uuid()
const id = uuid();
let { version = 0 } = getProps(newComponent);
version += 1;
if (typeOf(container) === "Flexbox") {
const [dim] = getManagedDimension(container.props.style);
const splitterSize = 6;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const size = { [dim]: (targetRect[dim] - splitterSize) / 2 };
const existingComponentStyle = getFlexOrIntrinsicStyle(
existingComponent,
Expand Down
3 changes: 1 addition & 2 deletions vuu-ui/packages/vuu-layout/src/layout-reducer/layoutUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { uuid } from "@finos/vuu-utils";
import { dimension, uuid } from "@finos/vuu-utils";
import React, { cloneElement, CSSProperties, ReactElement } from "react";
import { dimension } from "../common-types";
import {
ComponentWithId,
ComponentRegistry,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { dimension } from "@finos/vuu-utils";
import React, { CSSProperties, ReactElement } from "react";
import { dimension } from "../common-types";
import { followPath, getProps } from "../utils";
import { LayoutResizeAction, SplitterResizeAction } from "./layoutTypes";
import { swapChild } from "./replace-layout-element";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { uuid } from "@finos/vuu-utils";
import { rectTuple, uuid } from "@finos/vuu-utils";
import React, { ReactElement } from "react";
import { rectTuple } from "../common-types";
import { DropPos } from "../drag-drop/dragDropTypes";
import { DropTarget } from "../drag-drop/DropTarget";
import { ComponentRegistry } from "../registry/ComponentRegistry";
Expand All @@ -12,7 +11,7 @@ import {
flexDirection,
getFlexStyle,
getIntrinsicSize,
wrapIntrinsicSizeComponentWithFlexbox
wrapIntrinsicSizeComponentWithFlexbox,
} from "./flexUtils";
import { LayoutModel } from "./layoutTypes";
import { applyLayoutProps, LayoutProps } from "./layoutUtils";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
--overflow-direction: row;
--overflow-width: 0px;
--border-size: calc((var(--overflow-container-height) - 24px) / 2);
background-color: var(--vuuOverflowContainer-background, black);
background-color: var(--vuuOverflowContainer-background);
height: var(--overflow-container-height);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ const WrapContainer = React.memo(
orientation,
});

console.log(
`Overflow container WRAPPER ${React.Children.count(children)} children`
);

const height = orientation === "vertical" ? "100%" : `${heightProp}px`;
// TODO measure the height, if not provided
const style = {
Expand Down Expand Up @@ -137,6 +141,9 @@ export const OverflowContainer = forwardRef(function OverflowContainer(
forwardedRef: ForwardedRef<HTMLDivElement>
) {
const id = useId(idProp);

console.log(`Overflow container ${React.Children.count(children)} children`);

return (
<div
{...htmlAttributes}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@ export const useOverflowContainer = ({
let currentSize = 0;
return new ResizeObserver((entries: ResizeObserverEntry[]) => {
for (const entry of entries) {
const { [sizeProp]: size } = entry.contentRect;
const { [sizeProp]: actualSize } = entry.contentRect;
// This is important. Sometimes tiny sub-pixel differeces
// can be reported, which break the layout assumptions
const size = Math.round(actualSize as number);
if (isValidNumber(size) && currentSize !== size) {
currentSize = size;
handleResize();
Expand Down
5 changes: 4 additions & 1 deletion vuu-ui/packages/vuu-layout/src/palette/Palette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ export const PaletteItem = memo(
PaletteItem.displayName = "PaletteItem";

export interface PaletteProps
extends Omit<HTMLAttributes<HTMLDivElement>, "onSelect"> {
extends Omit<
HTMLAttributes<HTMLDivElement>,
"onDragStart" | "onDrop" | "onSelect"
> {
children: ReactElement[];
itemHeight?: number;
orientation: "horizontal" | "vertical";
Expand Down
5 changes: 4 additions & 1 deletion vuu-ui/packages/vuu-table/src/table-next/TableNext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { TableProps } from "@finos/vuu-table";
import { isGroupColumn, metadataKeys, notHidden } from "@finos/vuu-utils";
import cx from "classnames";
import { CSSProperties, useEffect, useRef } from "react";
import { GroupHeaderCell, HeaderCell } from "./header-cell";
import {
GroupHeaderCellNext as GroupHeaderCell,
HeaderCell,
} from "./header-cell";
import { Row as DefaultRow } from "./Row";
import { useTable } from "./useTableNext";
import { MeasuredContainer, useId } from "@finos/vuu-layout";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,23 @@ import { ColumnResizer, useTableColumnResize } from "../column-resizing";
import { HeaderCellProps } from "./HeaderCell";
import { useCell } from "../useCell";
import { ColumnHeaderPill, GroupColumnPill } from "../column-header-pill";
import { OverflowContainer } from "@finos/vuu-layout";
import { OverflowContainer, useLayoutEffectSkipFirst } from "@finos/vuu-layout";

import "./GroupHeaderCell.css";

const classBase = "vuuTableNextGroupHeaderCell";

const switchIfChanged = (
columns: KeyedColumnDescriptor[],
newColumns: KeyedColumnDescriptor[]
) => {
if (columns === newColumns) {
return columns;
} else {
return newColumns;
}
};

export interface GroupHeaderCellNextProps
extends Omit<HeaderCellProps, "onDragStart" | "onDrag" | "onDragEnd"> {
column: GroupColumnDescriptor;
Expand All @@ -27,7 +38,6 @@ export const GroupHeaderCellNext = ({
onResize,
...htmlAttributes
}: GroupHeaderCellNextProps) => {
console.log({ groupColumn });
const rootRef = useRef<HTMLTableCellElement>(null);
const { isResizing, ...resizeProps } = useTableColumnResize({
column: groupColumn,
Expand All @@ -36,9 +46,7 @@ export const GroupHeaderCellNext = ({
});

const [columns, setColumns] = useState(groupColumn.columns);

const { className, style } = useCell(groupColumn, classBase, true);

const columnPillProps =
columns.length > 1
? {
Expand All @@ -47,8 +55,7 @@ export const GroupHeaderCellNext = ({
}
: undefined;

const handleDrop = useCallback((fromIndex, toIndex) => {
console.log(`handle drop from ${fromIndex} to ${toIndex}`);
const handleMoveItem = useCallback((fromIndex, toIndex) => {
setColumns((cols) => {
const newCols = cols.slice();
const [tab] = newCols.splice(fromIndex, 1);
Expand All @@ -61,6 +68,10 @@ export const GroupHeaderCellNext = ({
});
}, []);

useLayoutEffectSkipFirst(() => {
setColumns((cols) => switchIfChanged(cols, groupColumn.columns));
}, [groupColumn.columns]);

return (
<div
{...htmlAttributes}
Expand All @@ -75,7 +86,7 @@ export const GroupHeaderCellNext = ({
allowDragDrop
className={`${classBase}-inner`}
height={24}
onMoveItem={handleDrop}
onMoveItem={handleMoveItem}
overflowPosition="start"
>
{columns.map((column) => {
Expand Down
Loading

0 comments on commit f6374c1

Please sign in to comment.