mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-08 21:15:18 +09:00
112 lines
3.8 KiB
TypeScript
112 lines
3.8 KiB
TypeScript
import { useEffect } from "react";
|
|
import {
|
|
type InstanceProps,
|
|
type Instance,
|
|
type Tree,
|
|
allUserPropsContainer,
|
|
} from "@webstudio-is/sdk";
|
|
import {
|
|
deleteInstanceMutable,
|
|
populateInstance,
|
|
findParentInstance,
|
|
findClosestSiblingInstance,
|
|
insertInstanceMutable,
|
|
} from "~/shared/tree-utils";
|
|
import store from "immerhin";
|
|
import { DropData } from "~/shared/component";
|
|
import {
|
|
rootInstanceContainer,
|
|
useRootInstance,
|
|
useSelectedInstance,
|
|
} from "./nano-values";
|
|
import { useSubscribe } from "./pubsub";
|
|
|
|
export const usePopulateRootInstance = (tree: Tree) => {
|
|
const [, setRootInstance] = useRootInstance();
|
|
useEffect(() => {
|
|
setRootInstance(tree.root);
|
|
}, [tree, setRootInstance]);
|
|
};
|
|
|
|
export const useInsertInstance = () => {
|
|
const [selectedInstance, setSelectedInstance] = useSelectedInstance();
|
|
|
|
useSubscribe<
|
|
"insertInstance",
|
|
{ instance: Instance; dropData?: DropData; props?: InstanceProps }
|
|
>("insertInstance", ({ instance, dropData, props }) => {
|
|
store.createTransaction(
|
|
[rootInstanceContainer, allUserPropsContainer],
|
|
(rootInstance, allUserProps) => {
|
|
if (rootInstance === undefined) return;
|
|
const populatedInstance = populateInstance(instance);
|
|
const hasInserted = insertInstanceMutable(
|
|
rootInstance,
|
|
populatedInstance,
|
|
{
|
|
parentId:
|
|
dropData?.instance.id ?? selectedInstance?.id ?? rootInstance.id,
|
|
position: dropData?.position || "end",
|
|
}
|
|
);
|
|
if (hasInserted) {
|
|
setSelectedInstance(instance);
|
|
}
|
|
if (props !== undefined) {
|
|
allUserProps[props.instanceId] = props;
|
|
}
|
|
}
|
|
);
|
|
});
|
|
};
|
|
|
|
export const useReparentInstance = () => {
|
|
useSubscribe<"reparentInstance", { instance: Instance; dropData: DropData }>(
|
|
"reparentInstance",
|
|
({ instance, dropData }) => {
|
|
store.createTransaction([rootInstanceContainer], (rootInstance) => {
|
|
if (rootInstance === undefined) return;
|
|
deleteInstanceMutable(rootInstance, instance.id);
|
|
insertInstanceMutable(rootInstance, instance, {
|
|
parentId: dropData.instance.id,
|
|
position: dropData.position,
|
|
});
|
|
});
|
|
}
|
|
);
|
|
};
|
|
|
|
export const useDeleteInstance = () => {
|
|
const [rootInstance] = useRootInstance();
|
|
const [selectedInstance, setSelectedInstance] = useSelectedInstance();
|
|
useSubscribe<"deleteInstance", { id: Instance["id"] }>(
|
|
"deleteInstance",
|
|
({ id }) => {
|
|
if (rootInstance !== undefined && selectedInstance !== undefined) {
|
|
const parentInstance = findParentInstance(rootInstance, id);
|
|
if (parentInstance !== undefined) {
|
|
const siblingInstance = findClosestSiblingInstance(
|
|
parentInstance,
|
|
id
|
|
);
|
|
setSelectedInstance(siblingInstance || parentInstance);
|
|
}
|
|
}
|
|
// @todo deleting instance should involve also deleting it's props
|
|
// If we don't delete them - they just live both on client and db
|
|
// Pros:
|
|
// - if we undo the deletion we don't need to undo the props deletion
|
|
// - in a multiplayer environment, some other user could have changed a prop while we have deleted the instance
|
|
// and then if we restore the instance, we would be restoring it with our props, potentially overwriting other users changes
|
|
// The way it is now it will actually still enable parallel deletion props editing and restoration.
|
|
// Contra: we are piling them up.
|
|
// Potentially we could also solve this by periodically removing unused props after while when instance was deleted
|
|
store.createTransaction([rootInstanceContainer], (rootInstance) => {
|
|
if (rootInstance !== undefined) {
|
|
deleteInstanceMutable(rootInstance, id);
|
|
}
|
|
});
|
|
}
|
|
);
|
|
};
|