waveterm/frontend/faraday/lib/utils.ts

140 lines
3.9 KiB
TypeScript
Raw Normal View History

// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { CSSProperties } from "react";
import { XYCoord } from "react-dnd";
export interface Dimensions {
width: number;
height: number;
left: number;
top: number;
}
export enum DropDirection {
Top = 0,
Right = 1,
Bottom = 2,
Left = 3,
Implement outer drop direction, add rudimentary drag preview image rendering (#29) This PR adds support for Outer variants of each DropDirection. When calculating the drop direction, the cursor position is calculated relevant to the box over which it is hovering. The following diagram shows how drop directions are calculated. The colored in center is currently not supported, it is assigned to the top, bottom, left, right direction for now, though it will ultimately be its own distinct direction. ![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97) When an outer drop direction is provided for a move operation, if the reference node flexes in the same axis as the drop direction, the new node will be inserted at the same level as the parent of the reference node. If the reference node flexes in a different direction or the reference node does not have a grandparent, the operation will fall back to its non-Outer variant. This also removes some chatty debug statements, adds a blur to the currently-dragging node to indicate that it cannot be dropped onto, and simplifies the deriving of the layout state atom from the tab atom so there's no longer another intermediate derived atom for the layout node. This also adds rudimentary support for rendering custom preview images for any tile being dragged. Right now, this is a simple block containing the block ID, but this can be anything. This resolves an issue where letting React-DnD generate its own previews could take up to a half second, and would block dragging until complete. For Monaco, this was outright failing. It also fixes an issue where the tile layout could animate on first paint. Now, I use React Suspense to prevent the layout from displaying until all the children have loaded.
2024-06-11 22:03:41 +02:00
OuterTop = 4,
OuterRight = 5,
OuterBottom = 6,
OuterLeft = 7,
Center = 8,
}
export enum FlexDirection {
Row = "row",
Column = "column",
}
export function reverseFlexDirection(flexDirection: FlexDirection): FlexDirection {
return flexDirection === FlexDirection.Row ? FlexDirection.Column : FlexDirection.Row;
}
export function determineDropDirection(dimensions?: Dimensions, offset?: XYCoord | null): DropDirection | undefined {
// console.log("determineDropDirection", dimensions, offset);
if (!offset || !dimensions) return undefined;
const { width, height, left, top } = dimensions;
let { x, y } = offset;
x -= left;
y -= top;
// Lies outside of the box
if (y < 0 || y > height || x < 0 || x > width) return undefined;
// Determines if a drop point falls within the center fifth of the box, meaning we should return Center.
const centerX1 = (2 * width) / 5;
const centerX2 = (3 * width) / 5;
const centerY1 = (2 * height) / 5;
const centerY2 = (3 * height) / 5;
Implement outer drop direction, add rudimentary drag preview image rendering (#29) This PR adds support for Outer variants of each DropDirection. When calculating the drop direction, the cursor position is calculated relevant to the box over which it is hovering. The following diagram shows how drop directions are calculated. The colored in center is currently not supported, it is assigned to the top, bottom, left, right direction for now, though it will ultimately be its own distinct direction. ![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97) When an outer drop direction is provided for a move operation, if the reference node flexes in the same axis as the drop direction, the new node will be inserted at the same level as the parent of the reference node. If the reference node flexes in a different direction or the reference node does not have a grandparent, the operation will fall back to its non-Outer variant. This also removes some chatty debug statements, adds a blur to the currently-dragging node to indicate that it cannot be dropped onto, and simplifies the deriving of the layout state atom from the tab atom so there's no longer another intermediate derived atom for the layout node. This also adds rudimentary support for rendering custom preview images for any tile being dragged. Right now, this is a simple block containing the block ID, but this can be anything. This resolves an issue where letting React-DnD generate its own previews could take up to a half second, and would block dragging until complete. For Monaco, this was outright failing. It also fixes an issue where the tile layout could animate on first paint. Now, I use React Suspense to prevent the layout from displaying until all the children have loaded.
2024-06-11 22:03:41 +02:00
if (x > centerX1 && x < centerX2 && y > centerY1 && y < centerY2) return DropDirection.Center;
Implement outer drop direction, add rudimentary drag preview image rendering (#29) This PR adds support for Outer variants of each DropDirection. When calculating the drop direction, the cursor position is calculated relevant to the box over which it is hovering. The following diagram shows how drop directions are calculated. The colored in center is currently not supported, it is assigned to the top, bottom, left, right direction for now, though it will ultimately be its own distinct direction. ![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97) When an outer drop direction is provided for a move operation, if the reference node flexes in the same axis as the drop direction, the new node will be inserted at the same level as the parent of the reference node. If the reference node flexes in a different direction or the reference node does not have a grandparent, the operation will fall back to its non-Outer variant. This also removes some chatty debug statements, adds a blur to the currently-dragging node to indicate that it cannot be dropped onto, and simplifies the deriving of the layout state atom from the tab atom so there's no longer another intermediate derived atom for the layout node. This also adds rudimentary support for rendering custom preview images for any tile being dragged. Right now, this is a simple block containing the block ID, but this can be anything. This resolves an issue where letting React-DnD generate its own previews could take up to a half second, and would block dragging until complete. For Monaco, this was outright failing. It also fixes an issue where the tile layout could animate on first paint. Now, I use React Suspense to prevent the layout from displaying until all the children have loaded.
2024-06-11 22:03:41 +02:00
const diagonal1 = y * width - x * height;
const diagonal2 = y * width + x * height - height * width;
// Lies on diagonal
if (diagonal1 == 0 || diagonal2 == 0) return undefined;
let code = 0;
if (diagonal2 > 0) {
code += 1;
}
if (diagonal1 > 0) {
code += 2;
code = 5 - code;
}
Implement outer drop direction, add rudimentary drag preview image rendering (#29) This PR adds support for Outer variants of each DropDirection. When calculating the drop direction, the cursor position is calculated relevant to the box over which it is hovering. The following diagram shows how drop directions are calculated. The colored in center is currently not supported, it is assigned to the top, bottom, left, right direction for now, though it will ultimately be its own distinct direction. ![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97) When an outer drop direction is provided for a move operation, if the reference node flexes in the same axis as the drop direction, the new node will be inserted at the same level as the parent of the reference node. If the reference node flexes in a different direction or the reference node does not have a grandparent, the operation will fall back to its non-Outer variant. This also removes some chatty debug statements, adds a blur to the currently-dragging node to indicate that it cannot be dropped onto, and simplifies the deriving of the layout state atom from the tab atom so there's no longer another intermediate derived atom for the layout node. This also adds rudimentary support for rendering custom preview images for any tile being dragged. Right now, this is a simple block containing the block ID, but this can be anything. This resolves an issue where letting React-DnD generate its own previews could take up to a half second, and would block dragging until complete. For Monaco, this was outright failing. It also fixes an issue where the tile layout could animate on first paint. Now, I use React Suspense to prevent the layout from displaying until all the children have loaded.
2024-06-11 22:03:41 +02:00
// Determines whether a drop is close to an edge of the box, meaning drop direction should be OuterX, instead of X
const xOuter1 = width / 5;
const xOuter2 = width - width / 5;
const yOuter1 = height / 5;
const yOuter2 = height - height / 5;
if (y < yOuter1 || y > yOuter2 || x < xOuter1 || x > xOuter2) {
code += 4;
}
return code;
}
export function setTransform({ top, left, width, height }: Dimensions, setSize: boolean = true): CSSProperties {
// Replace unitless items with px
const translate = `translate3d(${left}px,${top}px, 0)`;
return {
top: 0,
left: 0,
transform: translate,
WebkitTransform: translate,
MozTransform: translate,
msTransform: translate,
OTransform: translate,
width: setSize ? `${width}px` : undefined,
height: setSize ? `${height}px` : undefined,
position: "absolute",
};
}
export const debounce = <T extends (...args: any[]) => any>(callback: T, waitFor: number) => {
let timeout: NodeJS.Timeout;
return (...args: Parameters<T>): ReturnType<T> => {
let result: any;
clearTimeout(timeout);
timeout = setTimeout(() => {
result = callback(...args);
}, waitFor);
return result;
};
};
/**
* Simple wrapper function that lazily evaluates the provided function and caches its result for future calls.
* @param callback The function to lazily run.
* @returns The result of the function.
*/
export const lazy = <T extends (...args: any[]) => any>(callback: T) => {
let res: ReturnType<T>;
let processed = false;
return (...args: Parameters<T>): ReturnType<T> => {
if (processed) return res;
res = callback(...args);
processed = true;
return res;
};
};
/**
* Workaround for NodeJS compatibility. Will attempt to resolve the Crypto API from the browser and fallback to NodeJS if it isn't present.
* @returns The Crypto API.
*/
export function getCrypto() {
try {
return window.crypto;
} catch {
return crypto;
}
}