2024-06-04 22:05:44 +02:00
|
|
|
// Copyright 2024, Command Line Inc.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2024-06-12 02:42:10 +02:00
|
|
|
import { WOS } from "@/app/store/global";
|
2024-06-06 02:21:40 +02:00
|
|
|
import { Atom, Getter, PrimitiveAtom, WritableAtom, atom, useAtom } from "jotai";
|
2024-06-04 22:05:44 +02:00
|
|
|
import { useCallback } from "react";
|
2024-06-12 02:42:10 +02:00
|
|
|
import { layoutTreeStateReducer, newLayoutTreeState } from "./layoutState";
|
2024-06-04 22:05:44 +02:00
|
|
|
import {
|
|
|
|
LayoutNode,
|
2024-06-06 02:21:40 +02:00
|
|
|
LayoutNodeWaveObj,
|
2024-06-04 22:05:44 +02:00
|
|
|
LayoutTreeAction,
|
|
|
|
LayoutTreeState,
|
|
|
|
WritableLayoutNodeAtom,
|
|
|
|
WritableLayoutTreeStateAtom,
|
|
|
|
} from "./model.js";
|
|
|
|
|
2024-06-21 21:32:38 +02:00
|
|
|
// map from tabId => layout transforms (sizes and positions of the nodes)
|
|
|
|
let globalLayoutTransformsMap = new Map<string, Record<string, React.CSSProperties>>();
|
|
|
|
|
2024-06-04 22:05:44 +02:00
|
|
|
/**
|
|
|
|
* Creates a new layout tree state wrapped as an atom.
|
|
|
|
* @param rootNode The root node for the tree.
|
|
|
|
* @returns The state wrapped as an atom.
|
|
|
|
*
|
|
|
|
* @template T The type of data associated with the nodes of the tree.
|
|
|
|
*/
|
|
|
|
export function newLayoutTreeStateAtom<T>(rootNode: LayoutNode<T>): PrimitiveAtom<LayoutTreeState<T>> {
|
|
|
|
return atom(newLayoutTreeState(rootNode)) as PrimitiveAtom<LayoutTreeState<T>>;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Derives a WritableLayoutTreeStateAtom from a WritableLayoutNodeAtom, initializing the tree state.
|
|
|
|
* @param layoutNodeAtom The atom containing the root node for the LayoutTreeState.
|
|
|
|
* @returns The derived WritableLayoutTreeStateAtom.
|
|
|
|
*/
|
|
|
|
export function withLayoutTreeState<T>(layoutNodeAtom: WritableLayoutNodeAtom<T>): WritableLayoutTreeStateAtom<T> {
|
2024-06-06 02:21:40 +02:00
|
|
|
const pendingActionAtom = atom<LayoutTreeAction>(null) as PrimitiveAtom<LayoutTreeAction>;
|
2024-06-07 03:04:54 +02:00
|
|
|
const generationAtom = atom(0) as PrimitiveAtom<number>;
|
2024-06-04 22:05:44 +02:00
|
|
|
return atom(
|
|
|
|
(get) => {
|
2024-06-06 02:21:40 +02:00
|
|
|
const layoutState = newLayoutTreeState(get(layoutNodeAtom));
|
|
|
|
layoutState.pendingAction = get(pendingActionAtom);
|
2024-06-07 03:04:54 +02:00
|
|
|
layoutState.generation = get(generationAtom);
|
2024-06-06 02:21:40 +02:00
|
|
|
return layoutState;
|
2024-06-04 22:05:44 +02:00
|
|
|
},
|
2024-06-07 03:04:54 +02:00
|
|
|
(get, set, value) => {
|
2024-06-06 02:21:40 +02:00
|
|
|
set(pendingActionAtom, value.pendingAction);
|
2024-06-07 03:07:00 +02:00
|
|
|
if (get(generationAtom) !== value.generation) {
|
|
|
|
set(generationAtom, value.generation);
|
|
|
|
set(layoutNodeAtom, value.rootNode);
|
|
|
|
}
|
2024-06-04 22:05:44 +02:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hook to subscribe to the tree state and dispatch actions to its reducer functon.
|
|
|
|
* @param layoutTreeStateAtom The atom holding the layout tree state.
|
|
|
|
* @returns The current state of the tree and the dispatch function.
|
|
|
|
*/
|
|
|
|
export function useLayoutTreeStateReducerAtom<T>(
|
|
|
|
layoutTreeStateAtom: WritableLayoutTreeStateAtom<T>
|
|
|
|
): readonly [LayoutTreeState<T>, (action: LayoutTreeAction) => void] {
|
|
|
|
const [state, setState] = useAtom(layoutTreeStateAtom);
|
|
|
|
const dispatch = useCallback(
|
|
|
|
(action: LayoutTreeAction) => setState(layoutTreeStateReducer(state, action)),
|
|
|
|
[state, setState]
|
|
|
|
);
|
|
|
|
return [state, dispatch];
|
|
|
|
}
|
|
|
|
|
|
|
|
const tabLayoutAtomCache = new Map<string, WritableLayoutTreeStateAtom<TabLayoutData>>();
|
|
|
|
|
2024-06-06 02:21:40 +02:00
|
|
|
function getLayoutNodeWaveObjAtomFromTab<T>(
|
|
|
|
tabAtom: Atom<Tab>,
|
|
|
|
get: Getter
|
|
|
|
): WritableAtom<LayoutNodeWaveObj<T>, [value: LayoutNodeWaveObj<T>], void> {
|
|
|
|
const tabValue = get(tabAtom);
|
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
|
|
|
// console.log("getLayoutNodeWaveObjAtomFromTab tabValue", tabValue);
|
2024-06-06 02:21:40 +02:00
|
|
|
if (!tabValue) return;
|
|
|
|
const layoutNodeOref = WOS.makeORef("layout", tabValue.layoutNode);
|
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
|
|
|
// console.log("getLayoutNodeWaveObjAtomFromTab oref", layoutNodeOref);
|
2024-06-06 02:21:40 +02:00
|
|
|
return WOS.getWaveObjectAtom<LayoutNodeWaveObj<T>>(layoutNodeOref);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
export function withLayoutStateAtomFromTab<T>(tabAtom: Atom<Tab>): WritableLayoutTreeStateAtom<T> {
|
|
|
|
const pendingActionAtom = atom<LayoutTreeAction>(null) as PrimitiveAtom<LayoutTreeAction>;
|
|
|
|
const generationAtom = atom(0) as PrimitiveAtom<number>;
|
2024-06-06 02:21:40 +02:00
|
|
|
return atom(
|
|
|
|
(get) => {
|
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 waveObjAtom = getLayoutNodeWaveObjAtomFromTab<T>(tabAtom, get);
|
|
|
|
if (!waveObjAtom) return null;
|
|
|
|
const layoutState = newLayoutTreeState(get(waveObjAtom)?.node);
|
|
|
|
layoutState.pendingAction = get(pendingActionAtom);
|
|
|
|
layoutState.generation = get(generationAtom);
|
|
|
|
return layoutState;
|
2024-06-06 02:21:40 +02:00
|
|
|
},
|
|
|
|
(get, set, value) => {
|
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
|
|
|
set(pendingActionAtom, value.pendingAction);
|
|
|
|
if (get(generationAtom) !== value.generation) {
|
|
|
|
const waveObjAtom = getLayoutNodeWaveObjAtomFromTab<T>(tabAtom, get);
|
|
|
|
if (!waveObjAtom) return;
|
|
|
|
const newWaveObj = { ...get(waveObjAtom), node: value.rootNode };
|
|
|
|
set(generationAtom, value.generation);
|
|
|
|
set(waveObjAtom, newWaveObj);
|
|
|
|
}
|
2024-06-06 02:21:40 +02:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-06-04 22:05:44 +02:00
|
|
|
export function getLayoutStateAtomForTab(
|
|
|
|
tabId: string,
|
|
|
|
tabAtom: WritableAtom<Tab, [value: Tab], void>
|
|
|
|
): WritableLayoutTreeStateAtom<TabLayoutData> {
|
|
|
|
let atom = tabLayoutAtomCache.get(tabId);
|
|
|
|
if (atom) {
|
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
|
|
|
// console.log("Reusing atom for tab", tabId);
|
2024-06-04 22:05:44 +02:00
|
|
|
return atom;
|
|
|
|
}
|
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
|
|
|
// console.log("Creating new atom for tab", tabId);
|
|
|
|
atom = withLayoutStateAtomFromTab<TabLayoutData>(tabAtom);
|
2024-06-04 22:05:44 +02:00
|
|
|
tabLayoutAtomCache.set(tabId, atom);
|
|
|
|
return atom;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function deleteLayoutStateAtomForTab(tabId: string) {
|
|
|
|
const atom = tabLayoutAtomCache.get(tabId);
|
|
|
|
if (atom) {
|
|
|
|
tabLayoutAtomCache.delete(tabId);
|
|
|
|
}
|
|
|
|
}
|
2024-06-21 21:32:38 +02:00
|
|
|
|
|
|
|
export { globalLayoutTransformsMap };
|