155 lines
4.1 KiB
TypeScript
155 lines
4.1 KiB
TypeScript
import { CSSProperties } from "react";
|
|
import { XYCoord } from "react-dnd";
|
|
|
|
import { theme } from "constants/DefaultTheme";
|
|
import { WidgetProps, WidgetRowCols } from "widgets/BaseWidget";
|
|
import { isDropZoneOccupied } from "utils/WidgetPropsUtils";
|
|
import { OccupiedSpace } from "constants/editorConstants";
|
|
|
|
export type UIElementSize = { height: number; width: number };
|
|
|
|
export const RESIZABLE_CONTAINER_BORDER_THEME_INDEX = 1;
|
|
|
|
const RESIZE_HANDLE_HOVER_AREA_WIDTH = 12;
|
|
|
|
export const getHandleSyles = (): {
|
|
top: CSSProperties;
|
|
bottom: CSSProperties;
|
|
right: CSSProperties;
|
|
left: CSSProperties;
|
|
bottomRight: CSSProperties;
|
|
bottomLeft: CSSProperties;
|
|
} => {
|
|
const hoverWidth = RESIZE_HANDLE_HOVER_AREA_WIDTH;
|
|
const hoverWidthHalf = hoverWidth / 2;
|
|
const halfBorder =
|
|
theme.borders[RESIZABLE_CONTAINER_BORDER_THEME_INDEX].thickness / 2;
|
|
const shiftedHoverWidthHalf = hoverWidthHalf + halfBorder;
|
|
const hoverCornerWidth = hoverWidth + hoverWidth / 4;
|
|
|
|
return {
|
|
top: {
|
|
height: hoverWidth + "px",
|
|
top: "-" + shiftedHoverWidthHalf + "px",
|
|
zIndex: 1,
|
|
cursor: "ns-resize",
|
|
},
|
|
bottomRight: {
|
|
height: hoverCornerWidth + "px",
|
|
width: hoverCornerWidth + "px",
|
|
zIndex: 1,
|
|
cursor: "nwse-resize",
|
|
},
|
|
bottomLeft: {
|
|
height: hoverCornerWidth + "px",
|
|
width: hoverCornerWidth + "px",
|
|
zIndex: 1,
|
|
cursor: "nesw-resize",
|
|
},
|
|
bottom: {
|
|
height: hoverWidth + "px",
|
|
bottom: "-" + shiftedHoverWidthHalf + "px",
|
|
zIndex: 1,
|
|
cursor: "ns-resize",
|
|
},
|
|
left: {
|
|
width: hoverWidth + "px",
|
|
left: "-" + shiftedHoverWidthHalf + "px",
|
|
zIndex: 1,
|
|
cursor: "ew-resize",
|
|
},
|
|
right: {
|
|
width: hoverWidth + "px",
|
|
right: "-" + shiftedHoverWidthHalf + "px",
|
|
zIndex: 1,
|
|
cursor: "ew-resize",
|
|
},
|
|
};
|
|
};
|
|
|
|
export const computeUpdatedRowCols = (
|
|
isColliding: boolean,
|
|
delta: UIElementSize,
|
|
position: XYCoord,
|
|
props: WidgetProps,
|
|
): WidgetRowCols | false => {
|
|
if (isColliding) return false;
|
|
const newRowCols: WidgetRowCols = {
|
|
leftColumn: props.leftColumn + position.x / props.parentColumnSpace,
|
|
topRow: props.topRow + position.y / props.parentRowSpace,
|
|
|
|
rightColumn:
|
|
props.rightColumn + (delta.width + position.x) / props.parentColumnSpace,
|
|
bottomRow:
|
|
props.bottomRow + (delta.height + position.y) / props.parentRowSpace,
|
|
};
|
|
|
|
if (
|
|
props.leftColumn !== newRowCols.leftColumn ||
|
|
props.topRow !== newRowCols.topRow ||
|
|
props.bottomRow !== newRowCols.bottomRow ||
|
|
props.rightColumn !== newRowCols.rightColumn
|
|
) {
|
|
return newRowCols;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
export const hasCollision = (
|
|
delta: UIElementSize,
|
|
position: XYCoord,
|
|
props: WidgetProps,
|
|
occupiedSpaces?: OccupiedSpace[],
|
|
): boolean => {
|
|
const left = props.leftColumn + position.x / props.parentColumnSpace;
|
|
const top = props.topRow + position.y / props.parentRowSpace;
|
|
|
|
const right =
|
|
props.rightColumn + (delta.width + position.x) / props.parentColumnSpace;
|
|
const bottom =
|
|
props.bottomRow + (delta.height + position.y) / props.parentRowSpace;
|
|
|
|
return isDropZoneOccupied(
|
|
{
|
|
left,
|
|
top,
|
|
bottom,
|
|
right,
|
|
},
|
|
props.widgetId,
|
|
occupiedSpaces,
|
|
);
|
|
};
|
|
|
|
// TODO(abhinav): Memoize this?
|
|
export const getBorderStyles = (
|
|
isWidgetFocused: boolean,
|
|
isColliding: boolean,
|
|
padding: number,
|
|
): CSSProperties => {
|
|
const selectedColor = theme.colors.containerBorder;
|
|
const collisionColor = theme.colors.error;
|
|
|
|
// To fix the widget select/unselect size descripancy: Issue #127
|
|
// Always have the border, just toggle the opacity.
|
|
const unselectedColor = "transparent";
|
|
|
|
const borderThickness =
|
|
theme.borders[RESIZABLE_CONTAINER_BORDER_THEME_INDEX].thickness + "px";
|
|
const borderStyle =
|
|
theme.borders[RESIZABLE_CONTAINER_BORDER_THEME_INDEX].style;
|
|
|
|
let borderColor = unselectedColor;
|
|
if (isWidgetFocused) {
|
|
borderColor = selectedColor;
|
|
}
|
|
if (isColliding && isWidgetFocused) {
|
|
borderColor = collisionColor;
|
|
}
|
|
|
|
return {
|
|
border: [borderThickness, borderStyle, borderColor].join(" "),
|
|
padding: padding + "px",
|
|
};
|
|
};
|