@thatopen/services 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -62
- package/dist/built-in/index.d.ts +569 -0
- package/dist/cli.js +12 -23
- package/dist/index.cjs.js +2 -2
- package/dist/index.es.js +41 -49
- package/dist/vite-env.d.ts +1 -1
- package/docs/ai-quickstart.md +37 -0
- package/docs/builtin/paths.json +4 -0
- package/docs/cli/create.md +29 -29
- package/docs/cli/local-server.md +38 -38
- package/docs/cli/login.md +32 -32
- package/docs/cli/publish.md +31 -31
- package/docs/cli/run.md +26 -26
- package/docs/cli/serve.md +43 -43
- package/docs/cli/swap.md +30 -30
- package/docs/flow-architecture.md +389 -0
- package/docs/flow-plugin-guide.md +337 -0
- package/docs/platform-token.md +81 -0
- package/docs/revit-collab-quickstart.md +363 -173
- package/docs/rhino-interop-quickstart.md +391 -0
- package/package.json +1 -1
- package/resources/AGENTS.md +40 -40
- package/src/built-in/CDEManager/example.ts +214 -214
- package/src/built-in/CDEManager/examples/FileEditing/example.ts +226 -226
- package/src/built-in/CDEManager/examples/FileMoves/example.ts +186 -186
- package/src/built-in/CDEManager/examples/FileOperations/example.ts +295 -295
- package/src/built-in/CDEManager/examples/FileTransfers/example.ts +210 -210
- package/src/built-in/CDEManager/examples/FolderManagement/example.ts +230 -230
- package/src/built-in/CDEManager/examples/MetadataSchema/example.ts +230 -230
- package/src/built-in/CDEManager/examples/MetadataViews/example.ts +166 -166
- package/src/built-in/CDEManager/examples/ProjectConfig/example.ts +177 -177
- package/src/built-in/UIManager/src/app/example.ts +97 -97
- package/src/built-in/UIManager/src/app/examples/AppState/example.ts +140 -140
- package/src/built-in/UIManager/src/app/examples/Banner/example.ts +159 -159
- package/src/built-in/UIManager/src/app/examples/Contexts/example.ts +123 -123
- package/src/built-in/UIManager/src/app/examples/FileDrop/example.ts +124 -124
- package/src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts +90 -90
- package/src/built-in/UIManager/src/app/examples/Modal/example.ts +163 -163
- package/src/built-in/UIManager/src/app/examples/Notifications/example.ts +133 -133
- package/src/built-in/UIManager/src/app/examples/ProjectFileStorage/example.ts +150 -150
- package/src/built-in/UIManager/src/app/examples/Sidebar/example.ts +102 -102
- package/src/built-in/UIManager/src/clashes-panel/example.ts +183 -183
- package/src/built-in/UIManager/src/git-history-panel/example.ts +70 -0
- package/src/built-in/UIManager/src/models-list/example.ts +122 -122
- package/src/built-in/UIManager/src/topics-panel/example.ts +170 -170
- package/src/built-in/index.ts +570 -0
- package/src/cli/templates/app/index.html +35 -35
- package/src/cli/templates/app/package.json +32 -32
- package/src/cli/templates/app/src/bim-components/CloudRunner/index.ts +95 -95
- package/src/cli/templates/app/src/bim-components/CloudRunner/src/index.ts +1 -1
- package/src/cli/templates/app/src/bim-components/CloudRunner/src/types.ts +5 -5
- package/src/cli/templates/app/src/bim-components/index.ts +1 -1
- package/src/cli/templates/app/src/globals.ts +1 -1
- package/src/cli/templates/app/src/ui-components/app-info-section/index.ts +26 -26
- package/src/cli/templates/app/src/ui-components/app-info-section/src/index.ts +1 -1
- package/src/cli/templates/app/src/ui-components/app-info-section/src/types.ts +15 -15
- package/src/cli/templates/app/src/ui-components/cloud-runner-section/index.ts +37 -37
- package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/index.ts +1 -1
- package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/types.ts +14 -14
- package/src/cli/templates/app/src/ui-components/index.ts +2 -2
- package/src/cli/templates/app/tsconfig.json +16 -16
- package/src/cli/templates/app/vite.config.js +57 -57
- package/src/cli/templates/cloud-component/README.md +205 -205
- package/src/cli/templates/cloud-component/_thatopen +5 -5
- package/src/cli/templates/cloud-component/declarations.json +4 -4
- package/src/cli/templates/cloud-component/package.json +21 -21
- package/src/cli/templates/cloud-component/src/main.ts +70 -70
- package/src/cli/templates/cloud-component/tsconfig.json +16 -16
- package/src/cli/templates/cloud-component/vite.config.js +46 -46
- package/src/cli/templates/shared/AGENTS.md +8 -8
- package/src/cli/templates/shared/CLAUDE.md +1 -1
- package/src/cli/templates/shared/_gitignore +5 -5
|
@@ -1,186 +1,186 @@
|
|
|
1
|
-
/* MD
|
|
2
|
-
## Drag-and-drop file and folder moves
|
|
3
|
-
---
|
|
4
|
-
A coordinator restructures the project after a scope change: structural files move
|
|
5
|
-
from the root into a dedicated discipline folder, a MEP sub-folder needs to be
|
|
6
|
-
promoted to the top level, and a few documents need to come back to root to appear
|
|
7
|
-
in the general view. Drag-and-drop is the natural gesture for all of this, but
|
|
8
|
-
each drag session has intermediate stages — the file is "in the air", hovering over
|
|
9
|
-
a candidate target — and every panel watching the file table should see the right
|
|
10
|
-
state without coordinating directly with the drag component.
|
|
11
|
-
|
|
12
|
-
CDEManager models drag-and-drop as a sequence of typed state transitions. Picking
|
|
13
|
-
up files moves the machine to a dragging state that carries the files being dragged
|
|
14
|
-
and where they came from. Dropping onto a target kicks off the API call and advances
|
|
15
|
-
to a moving state. Releasing outside any drop zone cancels the drag and returns the
|
|
16
|
-
machine to exactly the state it was in before the drag started — idle or a selection.
|
|
17
|
-
Folder drags work the same way, with their own separate completion event.
|
|
18
|
-
|
|
19
|
-
This tutorial covers starting a file drag from idle and from an active selection;
|
|
20
|
-
cancelling the drag and confirming which state the machine returns to; committing
|
|
21
|
-
a drop onto a folder and reading the moved files from the completion event; moving
|
|
22
|
-
files back to the project root by passing a null target folder; and starting,
|
|
23
|
-
cancelling, and committing a folder drag.
|
|
24
|
-
|
|
25
|
-
By the end, you'll have a working demonstration of the full drag-and-drop cycle for
|
|
26
|
-
both files and folders — start, cancel, and commit — with all state transitions and
|
|
27
|
-
completion events verified against mock data.
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
import * as OBC from "@thatopen/components";
|
|
31
|
-
// In production platform apps, CDEManager is loaded through the platform client:
|
|
32
|
-
// const cde = await client.initBuiltInComponent(CDEManager.uuid, components);
|
|
33
|
-
// Here we import directly from source for local testing.
|
|
34
|
-
import { CDEManager } from "../../index";
|
|
35
|
-
import type { CDEFile } from "../../src/types";
|
|
36
|
-
|
|
37
|
-
/* MD
|
|
38
|
-
### 🛠️ Setting up
|
|
39
|
-
File and folder moves are permission-gated, so we simulate the platform startup step
|
|
40
|
-
that loads user permissions. The mock client also provides `updateItem` for file
|
|
41
|
-
moves and `resolveAccessToken` for folder moves, which use different backend paths.
|
|
42
|
-
*/
|
|
43
|
-
|
|
44
|
-
const components = new OBC.Components();
|
|
45
|
-
const cde = components.get(CDEManager);
|
|
46
|
-
|
|
47
|
-
const output = document.getElementById("output") as HTMLPreElement;
|
|
48
|
-
const log = (msg: string) => { console.log(msg); output.textContent += msg + "\n"; };
|
|
49
|
-
|
|
50
|
-
const mockClient = {
|
|
51
|
-
checkPermissionBatch: async () => Array(5).fill({ hasPermission: true }),
|
|
52
|
-
getProjectData: async () => ({ project: { title: "" }, users: [] }),
|
|
53
|
-
listFolders: async () => [],
|
|
54
|
-
updateItem: async (_id: string, _updates: any) => ({}),
|
|
55
|
-
resolveAccessToken: async () => "mock-token",
|
|
56
|
-
context: { apiUrl: "https://mock.api" },
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
cde.ctx = { client: mockClient as any, projectId: "mock" };
|
|
60
|
-
await cde.loadFolders();
|
|
61
|
-
|
|
62
|
-
const folders = [
|
|
63
|
-
{ _id: "folder-str", name: "Structural", parentId: null },
|
|
64
|
-
{ _id: "folder-arch", name: "Architecture", parentId: null },
|
|
65
|
-
{ _id: "folder-mep", name: "MEP", parentId: "folder-arch" },
|
|
66
|
-
];
|
|
67
|
-
cde.folders = folders as any;
|
|
68
|
-
|
|
69
|
-
const now = new Date();
|
|
70
|
-
const mockFiles: CDEFile[] = [
|
|
71
|
-
{ _id: "f1", name: "column.ifc", folderId: null, createdBy: "alice", createdAt: now, metadata: {} } as CDEFile,
|
|
72
|
-
{ _id: "f2", name: "foundations.ifc", folderId: null, createdBy: "alice", createdAt: now, metadata: {} } as CDEFile,
|
|
73
|
-
{ _id: "f3", name: "duct.ifc", folderId: "folder-arch", createdBy: "bob", createdAt: now, metadata: {} } as CDEFile,
|
|
74
|
-
];
|
|
75
|
-
cde.files = mockFiles;
|
|
76
|
-
|
|
77
|
-
cde.onMachineStateChanged.add((s) => log(` → ${s.kind}`));
|
|
78
|
-
cde.onFilesMovedCompleted.add(({ files, targetFolderName }) =>
|
|
79
|
-
log(` [onFilesMovedCompleted] ${files.length} file(s) → "${targetFolderName ?? "root"}"`)
|
|
80
|
-
);
|
|
81
|
-
cde.onFolderMovedCompleted.add(({ folderName, targetFolderName }) =>
|
|
82
|
-
log(` [onFolderMovedCompleted] "${folderName}" → "${targetFolderName ?? "root"}"`)
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
/* MD
|
|
86
|
-
### 🖱️ File drag — cancel
|
|
87
|
-
Starting a drag records the dragged files and the folder they came from. Cancelling
|
|
88
|
-
— for example when the user releases the mouse outside any drop target — returns the
|
|
89
|
-
machine to the state it was in when the drag started. If the drag started from idle,
|
|
90
|
-
it returns to idle. If it started from a selection, the same selection is restored.
|
|
91
|
-
*/
|
|
92
|
-
|
|
93
|
-
log("\n--- file drag: cancel from idle ---");
|
|
94
|
-
cde.sendMachineEvent({ type: "START_DRAG", files: [mockFiles[0]], sourceFolderId: null });
|
|
95
|
-
|
|
96
|
-
if (cde.machineState.kind === "dragging") {
|
|
97
|
-
log(` dragging: ${cde.machineState.files.length} file(s) from "${cde.machineState.sourceFolderId ?? "root"}"`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
cde.sendMachineEvent({ type: "END_DRAG" });
|
|
101
|
-
log(` state after cancel: ${cde.machineState.kind}`);
|
|
102
|
-
|
|
103
|
-
log("\n--- file drag: cancel from selection ---");
|
|
104
|
-
cde.sendMachineEvent({ type: "SELECT", files: [mockFiles[0], mockFiles[1]] });
|
|
105
|
-
// Drag starts from "selected" — previousState carries the selection.
|
|
106
|
-
cde.sendMachineEvent({ type: "START_DRAG", files: [mockFiles[0]], sourceFolderId: null });
|
|
107
|
-
cde.sendMachineEvent({ type: "END_DRAG" });
|
|
108
|
-
log(` state after cancel: ${cde.machineState.kind}`);
|
|
109
|
-
if (cde.machineState.kind === "selected") {
|
|
110
|
-
log(` selection preserved: ${cde.machineState.files.length} file(s)`);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/* MD
|
|
114
|
-
### 📁 File drag — drop onto a folder
|
|
115
|
-
When the user drops files onto a valid folder target, the machine moves to a moving
|
|
116
|
-
state and the API call runs. On completion, the moved files reflect their new folder
|
|
117
|
-
and the machine returns to idle. The completion event carries both the updated files
|
|
118
|
-
and the target folder name, so the UI can show a confirmation without reading the
|
|
119
|
-
whole file list.
|
|
120
|
-
*/
|
|
121
|
-
|
|
122
|
-
log("\n--- file drag: drop onto folder ---");
|
|
123
|
-
cde.sendMachineEvent({ type: "DESELECT" });
|
|
124
|
-
cde.sendMachineEvent({ type: "START_DRAG", files: [mockFiles[0], mockFiles[1]], sourceFolderId: null });
|
|
125
|
-
|
|
126
|
-
await new Promise<void>(resolve => {
|
|
127
|
-
cde.onFilesMovedCompleted.add(() => resolve());
|
|
128
|
-
cde.sendMachineEvent({ type: "DROP_FILES", targetFolderId: "folder-str" });
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
log(` state after move: ${cde.machineState.kind}`);
|
|
132
|
-
const f1Updated = cde.files.find(f => f._id === "f1")!;
|
|
133
|
-
log(` f1 folderId: ${(f1Updated as any).folderId}`);
|
|
134
|
-
|
|
135
|
-
/* MD
|
|
136
|
-
### 🏠 File drag — drop at root
|
|
137
|
-
Dropping files without specifying a target folder moves them to the project root.
|
|
138
|
-
The completion event's `targetFolderName` is null for root drops, which the UI can
|
|
139
|
-
use to display "Moved to root" instead of a folder name.
|
|
140
|
-
*/
|
|
141
|
-
|
|
142
|
-
log("\n--- file drag: drop at root ---");
|
|
143
|
-
cde.sendMachineEvent({ type: "START_DRAG", files: [mockFiles[2]], sourceFolderId: "folder-arch" });
|
|
144
|
-
|
|
145
|
-
await new Promise<void>(resolve => {
|
|
146
|
-
cde.onFilesMovedCompleted.add(() => resolve());
|
|
147
|
-
cde.sendMachineEvent({ type: "DROP_FILES", targetFolderId: null });
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
log(` state after move: ${cde.machineState.kind}`);
|
|
151
|
-
const f3Updated = cde.files.find(f => f._id === "f3")!;
|
|
152
|
-
log(` f3 folderId: ${(f3Updated as any).folderId ?? "root"}`);
|
|
153
|
-
|
|
154
|
-
/* MD
|
|
155
|
-
### 📂 Folder drag — cancel and commit
|
|
156
|
-
Folder drags follow the same state sequence as file drags. Cancelling a folder drag
|
|
157
|
-
returns to the previous state unchanged. Committing calls a dedicated backend
|
|
158
|
-
endpoint and fires a separate completion event with the folder names involved, which
|
|
159
|
-
the UI uses for confirmation messages.
|
|
160
|
-
*/
|
|
161
|
-
|
|
162
|
-
log("\n--- folder drag: cancel ---");
|
|
163
|
-
cde.sendMachineEvent({ type: "START_FOLDER_DRAG", folderId: "folder-mep", parentFolderId: "folder-arch" });
|
|
164
|
-
|
|
165
|
-
if (cde.machineState.kind === "draggingFolder") {
|
|
166
|
-
log(` draggingFolder: "${cde.machineState.folderId}"`);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
cde.sendMachineEvent({ type: "END_FOLDER_DRAG" });
|
|
170
|
-
log(` state after cancel: ${cde.machineState.kind}`);
|
|
171
|
-
|
|
172
|
-
log("\n--- folder drag: commit ---");
|
|
173
|
-
// _moveFolder calls fetch directly; intercept it for the mock.
|
|
174
|
-
const realFetch = globalThis.fetch;
|
|
175
|
-
globalThis.fetch = async () => new Response("{}", { status: 200 });
|
|
176
|
-
|
|
177
|
-
cde.sendMachineEvent({ type: "START_FOLDER_DRAG", folderId: "folder-mep", parentFolderId: "folder-arch" });
|
|
178
|
-
|
|
179
|
-
await new Promise<void>(resolve => {
|
|
180
|
-
cde.onFolderMovedCompleted.add(() => resolve());
|
|
181
|
-
// Move MEP out of Architecture and into Structural.
|
|
182
|
-
cde.sendMachineEvent({ type: "DROP_FOLDER", targetFolderId: "folder-str" });
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
globalThis.fetch = realFetch;
|
|
186
|
-
log(` state after commit: ${cde.machineState.kind}`);
|
|
1
|
+
/* MD
|
|
2
|
+
## Drag-and-drop file and folder moves
|
|
3
|
+
---
|
|
4
|
+
A coordinator restructures the project after a scope change: structural files move
|
|
5
|
+
from the root into a dedicated discipline folder, a MEP sub-folder needs to be
|
|
6
|
+
promoted to the top level, and a few documents need to come back to root to appear
|
|
7
|
+
in the general view. Drag-and-drop is the natural gesture for all of this, but
|
|
8
|
+
each drag session has intermediate stages — the file is "in the air", hovering over
|
|
9
|
+
a candidate target — and every panel watching the file table should see the right
|
|
10
|
+
state without coordinating directly with the drag component.
|
|
11
|
+
|
|
12
|
+
CDEManager models drag-and-drop as a sequence of typed state transitions. Picking
|
|
13
|
+
up files moves the machine to a dragging state that carries the files being dragged
|
|
14
|
+
and where they came from. Dropping onto a target kicks off the API call and advances
|
|
15
|
+
to a moving state. Releasing outside any drop zone cancels the drag and returns the
|
|
16
|
+
machine to exactly the state it was in before the drag started — idle or a selection.
|
|
17
|
+
Folder drags work the same way, with their own separate completion event.
|
|
18
|
+
|
|
19
|
+
This tutorial covers starting a file drag from idle and from an active selection;
|
|
20
|
+
cancelling the drag and confirming which state the machine returns to; committing
|
|
21
|
+
a drop onto a folder and reading the moved files from the completion event; moving
|
|
22
|
+
files back to the project root by passing a null target folder; and starting,
|
|
23
|
+
cancelling, and committing a folder drag.
|
|
24
|
+
|
|
25
|
+
By the end, you'll have a working demonstration of the full drag-and-drop cycle for
|
|
26
|
+
both files and folders — start, cancel, and commit — with all state transitions and
|
|
27
|
+
completion events verified against mock data.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import * as OBC from "@thatopen/components";
|
|
31
|
+
// In production platform apps, CDEManager is loaded through the platform client:
|
|
32
|
+
// const cde = await client.initBuiltInComponent(CDEManager.uuid, components);
|
|
33
|
+
// Here we import directly from source for local testing.
|
|
34
|
+
import { CDEManager } from "../../index";
|
|
35
|
+
import type { CDEFile } from "../../src/types";
|
|
36
|
+
|
|
37
|
+
/* MD
|
|
38
|
+
### 🛠️ Setting up
|
|
39
|
+
File and folder moves are permission-gated, so we simulate the platform startup step
|
|
40
|
+
that loads user permissions. The mock client also provides `updateItem` for file
|
|
41
|
+
moves and `resolveAccessToken` for folder moves, which use different backend paths.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
const components = new OBC.Components();
|
|
45
|
+
const cde = components.get(CDEManager);
|
|
46
|
+
|
|
47
|
+
const output = document.getElementById("output") as HTMLPreElement;
|
|
48
|
+
const log = (msg: string) => { console.log(msg); output.textContent += msg + "\n"; };
|
|
49
|
+
|
|
50
|
+
const mockClient = {
|
|
51
|
+
checkPermissionBatch: async () => Array(5).fill({ hasPermission: true }),
|
|
52
|
+
getProjectData: async () => ({ project: { title: "" }, users: [] }),
|
|
53
|
+
listFolders: async () => [],
|
|
54
|
+
updateItem: async (_id: string, _updates: any) => ({}),
|
|
55
|
+
resolveAccessToken: async () => "mock-token",
|
|
56
|
+
context: { apiUrl: "https://mock.api" },
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
cde.ctx = { client: mockClient as any, projectId: "mock" };
|
|
60
|
+
await cde.loadFolders();
|
|
61
|
+
|
|
62
|
+
const folders = [
|
|
63
|
+
{ _id: "folder-str", name: "Structural", parentId: null },
|
|
64
|
+
{ _id: "folder-arch", name: "Architecture", parentId: null },
|
|
65
|
+
{ _id: "folder-mep", name: "MEP", parentId: "folder-arch" },
|
|
66
|
+
];
|
|
67
|
+
cde.folders = folders as any;
|
|
68
|
+
|
|
69
|
+
const now = new Date();
|
|
70
|
+
const mockFiles: CDEFile[] = [
|
|
71
|
+
{ _id: "f1", name: "column.ifc", folderId: null, createdBy: "alice", createdAt: now, metadata: {} } as CDEFile,
|
|
72
|
+
{ _id: "f2", name: "foundations.ifc", folderId: null, createdBy: "alice", createdAt: now, metadata: {} } as CDEFile,
|
|
73
|
+
{ _id: "f3", name: "duct.ifc", folderId: "folder-arch", createdBy: "bob", createdAt: now, metadata: {} } as CDEFile,
|
|
74
|
+
];
|
|
75
|
+
cde.files = mockFiles;
|
|
76
|
+
|
|
77
|
+
cde.onMachineStateChanged.add((s) => log(` → ${s.kind}`));
|
|
78
|
+
cde.onFilesMovedCompleted.add(({ files, targetFolderName }) =>
|
|
79
|
+
log(` [onFilesMovedCompleted] ${files.length} file(s) → "${targetFolderName ?? "root"}"`)
|
|
80
|
+
);
|
|
81
|
+
cde.onFolderMovedCompleted.add(({ folderName, targetFolderName }) =>
|
|
82
|
+
log(` [onFolderMovedCompleted] "${folderName}" → "${targetFolderName ?? "root"}"`)
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
/* MD
|
|
86
|
+
### 🖱️ File drag — cancel
|
|
87
|
+
Starting a drag records the dragged files and the folder they came from. Cancelling
|
|
88
|
+
— for example when the user releases the mouse outside any drop target — returns the
|
|
89
|
+
machine to the state it was in when the drag started. If the drag started from idle,
|
|
90
|
+
it returns to idle. If it started from a selection, the same selection is restored.
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
log("\n--- file drag: cancel from idle ---");
|
|
94
|
+
cde.sendMachineEvent({ type: "START_DRAG", files: [mockFiles[0]], sourceFolderId: null });
|
|
95
|
+
|
|
96
|
+
if (cde.machineState.kind === "dragging") {
|
|
97
|
+
log(` dragging: ${cde.machineState.files.length} file(s) from "${cde.machineState.sourceFolderId ?? "root"}"`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
cde.sendMachineEvent({ type: "END_DRAG" });
|
|
101
|
+
log(` state after cancel: ${cde.machineState.kind}`);
|
|
102
|
+
|
|
103
|
+
log("\n--- file drag: cancel from selection ---");
|
|
104
|
+
cde.sendMachineEvent({ type: "SELECT", files: [mockFiles[0], mockFiles[1]] });
|
|
105
|
+
// Drag starts from "selected" — previousState carries the selection.
|
|
106
|
+
cde.sendMachineEvent({ type: "START_DRAG", files: [mockFiles[0]], sourceFolderId: null });
|
|
107
|
+
cde.sendMachineEvent({ type: "END_DRAG" });
|
|
108
|
+
log(` state after cancel: ${cde.machineState.kind}`);
|
|
109
|
+
if (cde.machineState.kind === "selected") {
|
|
110
|
+
log(` selection preserved: ${cde.machineState.files.length} file(s)`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* MD
|
|
114
|
+
### 📁 File drag — drop onto a folder
|
|
115
|
+
When the user drops files onto a valid folder target, the machine moves to a moving
|
|
116
|
+
state and the API call runs. On completion, the moved files reflect their new folder
|
|
117
|
+
and the machine returns to idle. The completion event carries both the updated files
|
|
118
|
+
and the target folder name, so the UI can show a confirmation without reading the
|
|
119
|
+
whole file list.
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
log("\n--- file drag: drop onto folder ---");
|
|
123
|
+
cde.sendMachineEvent({ type: "DESELECT" });
|
|
124
|
+
cde.sendMachineEvent({ type: "START_DRAG", files: [mockFiles[0], mockFiles[1]], sourceFolderId: null });
|
|
125
|
+
|
|
126
|
+
await new Promise<void>(resolve => {
|
|
127
|
+
cde.onFilesMovedCompleted.add(() => resolve());
|
|
128
|
+
cde.sendMachineEvent({ type: "DROP_FILES", targetFolderId: "folder-str" });
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
log(` state after move: ${cde.machineState.kind}`);
|
|
132
|
+
const f1Updated = cde.files.find(f => f._id === "f1")!;
|
|
133
|
+
log(` f1 folderId: ${(f1Updated as any).folderId}`);
|
|
134
|
+
|
|
135
|
+
/* MD
|
|
136
|
+
### 🏠 File drag — drop at root
|
|
137
|
+
Dropping files without specifying a target folder moves them to the project root.
|
|
138
|
+
The completion event's `targetFolderName` is null for root drops, which the UI can
|
|
139
|
+
use to display "Moved to root" instead of a folder name.
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
log("\n--- file drag: drop at root ---");
|
|
143
|
+
cde.sendMachineEvent({ type: "START_DRAG", files: [mockFiles[2]], sourceFolderId: "folder-arch" });
|
|
144
|
+
|
|
145
|
+
await new Promise<void>(resolve => {
|
|
146
|
+
cde.onFilesMovedCompleted.add(() => resolve());
|
|
147
|
+
cde.sendMachineEvent({ type: "DROP_FILES", targetFolderId: null });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
log(` state after move: ${cde.machineState.kind}`);
|
|
151
|
+
const f3Updated = cde.files.find(f => f._id === "f3")!;
|
|
152
|
+
log(` f3 folderId: ${(f3Updated as any).folderId ?? "root"}`);
|
|
153
|
+
|
|
154
|
+
/* MD
|
|
155
|
+
### 📂 Folder drag — cancel and commit
|
|
156
|
+
Folder drags follow the same state sequence as file drags. Cancelling a folder drag
|
|
157
|
+
returns to the previous state unchanged. Committing calls a dedicated backend
|
|
158
|
+
endpoint and fires a separate completion event with the folder names involved, which
|
|
159
|
+
the UI uses for confirmation messages.
|
|
160
|
+
*/
|
|
161
|
+
|
|
162
|
+
log("\n--- folder drag: cancel ---");
|
|
163
|
+
cde.sendMachineEvent({ type: "START_FOLDER_DRAG", folderId: "folder-mep", parentFolderId: "folder-arch" });
|
|
164
|
+
|
|
165
|
+
if (cde.machineState.kind === "draggingFolder") {
|
|
166
|
+
log(` draggingFolder: "${cde.machineState.folderId}"`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
cde.sendMachineEvent({ type: "END_FOLDER_DRAG" });
|
|
170
|
+
log(` state after cancel: ${cde.machineState.kind}`);
|
|
171
|
+
|
|
172
|
+
log("\n--- folder drag: commit ---");
|
|
173
|
+
// _moveFolder calls fetch directly; intercept it for the mock.
|
|
174
|
+
const realFetch = globalThis.fetch;
|
|
175
|
+
globalThis.fetch = async () => new Response("{}", { status: 200 });
|
|
176
|
+
|
|
177
|
+
cde.sendMachineEvent({ type: "START_FOLDER_DRAG", folderId: "folder-mep", parentFolderId: "folder-arch" });
|
|
178
|
+
|
|
179
|
+
await new Promise<void>(resolve => {
|
|
180
|
+
cde.onFolderMovedCompleted.add(() => resolve());
|
|
181
|
+
// Move MEP out of Architecture and into Structural.
|
|
182
|
+
cde.sendMachineEvent({ type: "DROP_FOLDER", targetFolderId: "folder-str" });
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
globalThis.fetch = realFetch;
|
|
186
|
+
log(` state after commit: ${cde.machineState.kind}`);
|