@arronqzy/react-view 1.0.38 → 1.0.40
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/dist/styles.css +316 -241
- package/package.json +5 -5
- package/readme.md +15 -12
- package/src/index.ts +23 -1
- package/src/panel/ReactViewPanel.tsx +140 -90
- package/src/panel/components/ElementsLayer.tsx +1 -1
- package/src/panel/components/MaterialSidebar.tsx +304 -264
- package/src/panel/components/MoveableLayer.tsx +11 -2
- package/src/panel/components/PanelCanvas.tsx +1 -0
- package/src/panel/components/PanelConfigSidebar.tsx +49 -65
- package/src/panel/components/PanelLayerDock.tsx +397 -316
- package/src/panel/components/PanelRulers.tsx +169 -42
- package/src/panel/components/SelectLayer.tsx +2 -2
- package/src/panel/components/WorkspaceProjectNav.tsx +1 -1
- package/src/panel/hooks/useWorkspaceProjects.ts +228 -52
- package/src/panel/icons.tsx +117 -0
- package/src/panel/library/abuilder-storage.ts +101 -3
- package/src/panel/library/abuilder-view-handle.ts +100 -0
- package/src/panel/library/event-subscription.ts +70 -5
- package/src/panel/library/workspace-project-sync.ts +7 -1
- package/src/panel/library/workspace-storage.ts +58 -4
- package/src/tailwind.css +66 -5
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { normalizeAppNameSpace } from "@arronqzy/blueprint-dsl";
|
|
2
|
+
import type { WorkspaceProjectRecord } from "./workspace-project-db";
|
|
3
|
+
|
|
4
|
+
/** 已挂载编辑面板的命令式句柄(也可通过 `ref` 拿到) */
|
|
5
|
+
export type AbuilderViewHandle = {
|
|
6
|
+
/** 当前 nameSpace */
|
|
7
|
+
getNameSpace: () => string | null;
|
|
8
|
+
/** 导出当前编辑器中的完整工作区;尚无关联 id 时返回 null */
|
|
9
|
+
getCurrentWorkspace: () => WorkspaceProjectRecord | null;
|
|
10
|
+
/** 按 id 从 IndexedDB 打开,或直接应用完整记录 */
|
|
11
|
+
openWorkspace: (idOrRecord: string | WorkspaceProjectRecord) => Promise<void>;
|
|
12
|
+
/** 创建新工作区并写入 IndexedDB,发出 workspace:add */
|
|
13
|
+
createWorkspace: () => Promise<{ name: string; id: string }>;
|
|
14
|
+
/** 保存当前工作区到 IndexedDB,发出 workspace:sync;无 active 时等同创建 */
|
|
15
|
+
saveWorkspace: () => Promise<{ name: string; id: string }>;
|
|
16
|
+
/** 同步当前工作区到 IndexedDB,发出 workspace:sync */
|
|
17
|
+
syncWorkspace: () => Promise<string>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const handles = new Set<AbuilderViewHandle>();
|
|
21
|
+
|
|
22
|
+
export function registerAbuilderViewHandle(handle: AbuilderViewHandle): () => void {
|
|
23
|
+
handles.add(handle);
|
|
24
|
+
return () => {
|
|
25
|
+
handles.delete(handle);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function findHandle(nameSpace?: string | null): AbuilderViewHandle | null {
|
|
30
|
+
if (handles.size === 0) return null;
|
|
31
|
+
if (nameSpace === undefined) {
|
|
32
|
+
return handles.values().next().value ?? null;
|
|
33
|
+
}
|
|
34
|
+
const wanted = normalizeAppNameSpace(nameSpace);
|
|
35
|
+
for (const handle of handles) {
|
|
36
|
+
if (normalizeAppNameSpace(handle.getNameSpace()) === wanted) return handle;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 读取当前已挂载编辑器中的工作区快照。
|
|
43
|
+
* 同页多个实例时请传入 `nameSpace`;未挂载或尚无 active 工作区时返回 null。
|
|
44
|
+
*/
|
|
45
|
+
export function getCurrentWorkspace(
|
|
46
|
+
nameSpace?: string | null
|
|
47
|
+
): WorkspaceProjectRecord | null {
|
|
48
|
+
return findHandle(nameSpace)?.getCurrentWorkspace() ?? null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 命令式打开工作区(不 remount)。
|
|
53
|
+
* 同页多个实例时请传入 `nameSpace`。
|
|
54
|
+
*/
|
|
55
|
+
export async function openWorkspace(
|
|
56
|
+
idOrRecord: string | WorkspaceProjectRecord,
|
|
57
|
+
nameSpace?: string | null
|
|
58
|
+
): Promise<void> {
|
|
59
|
+
const handle = findHandle(nameSpace);
|
|
60
|
+
if (!handle) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
"No mounted Abuilder panel. Pass initialWorkspace or mount <App> first."
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
await handle.openWorkspace(idOrRecord);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function createWorkspace(
|
|
69
|
+
nameSpace?: string | null
|
|
70
|
+
): Promise<{ name: string; id: string }> {
|
|
71
|
+
const handle = findHandle(nameSpace);
|
|
72
|
+
if (!handle) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
"No mounted Abuilder panel. Pass initialWorkspace or mount <App> first."
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
return handle.createWorkspace();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function saveWorkspace(
|
|
81
|
+
nameSpace?: string | null
|
|
82
|
+
): Promise<{ name: string; id: string }> {
|
|
83
|
+
const handle = findHandle(nameSpace);
|
|
84
|
+
if (!handle) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
"No mounted Abuilder panel. Pass initialWorkspace or mount <App> first."
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return handle.saveWorkspace();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function syncWorkspace(nameSpace?: string | null): Promise<string> {
|
|
93
|
+
const handle = findHandle(nameSpace);
|
|
94
|
+
if (!handle) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
"No mounted Abuilder panel. Pass initialWorkspace or mount <App> first."
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return handle.syncWorkspace();
|
|
100
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeAppNameSpace } from "@arronqzy/blueprint-dsl";
|
|
1
2
|
import type { WorkspaceProjectRecord } from "./workspace-project-db";
|
|
2
3
|
|
|
3
4
|
export const AbuilderEvents = {
|
|
@@ -7,10 +8,14 @@ export const AbuilderEvents = {
|
|
|
7
8
|
workspaceSync: "workspace:sync",
|
|
8
9
|
/** 删除工作区(界面删除,或调用 deleteWorkspaceProject / clearWorkspaceProjects) */
|
|
9
10
|
workspaceDelete: "workspace:delete",
|
|
11
|
+
/** 蓝图库写入 / 删除 / 清空 */
|
|
12
|
+
blueprintLibraryChanged: "blueprint-library:changed",
|
|
10
13
|
/** 开始上传接口集合 */
|
|
11
14
|
apiCollectionUploadStart: "api-collection:upload-start",
|
|
12
15
|
/** 上传接口集合成功 */
|
|
13
16
|
apiCollectionUploadSuccess: "api-collection:upload-success",
|
|
17
|
+
/** 接口集合写入 / 删除 / 清空 */
|
|
18
|
+
apiCollectionChanged: "api-collection:changed",
|
|
14
19
|
} as const;
|
|
15
20
|
|
|
16
21
|
export type AbuilderEventName = (typeof AbuilderEvents)[keyof typeof AbuilderEvents];
|
|
@@ -18,8 +23,14 @@ export type AbuilderEventName = (typeof AbuilderEvents)[keyof typeof AbuilderEve
|
|
|
18
23
|
/** 工作区完整数据(面板 + 蓝图),可用于外部持久化与再次加载 */
|
|
19
24
|
export type WorkspaceData = WorkspaceProjectRecord;
|
|
20
25
|
|
|
21
|
-
export type WorkspaceAddEventPayload = WorkspaceData
|
|
22
|
-
|
|
26
|
+
export type WorkspaceAddEventPayload = WorkspaceData & {
|
|
27
|
+
/** 所属 nameSpace,默认库为 null */
|
|
28
|
+
nameSpace: string | null;
|
|
29
|
+
};
|
|
30
|
+
export type WorkspaceSyncEventPayload = WorkspaceData & {
|
|
31
|
+
/** 所属 nameSpace,默认库为 null */
|
|
32
|
+
nameSpace: string | null;
|
|
33
|
+
};
|
|
23
34
|
export type WorkspaceDeleteEventPayload = {
|
|
24
35
|
/** 被删除的工作区 id */
|
|
25
36
|
ids: string[];
|
|
@@ -27,9 +38,17 @@ export type WorkspaceDeleteEventPayload = {
|
|
|
27
38
|
nameSpace: string | null;
|
|
28
39
|
};
|
|
29
40
|
|
|
41
|
+
export type BlueprintLibraryChangedEventPayload = {
|
|
42
|
+
nameSpace: string | null;
|
|
43
|
+
action: "put" | "delete" | "clear";
|
|
44
|
+
/** 涉及的蓝图 id;clear 时可能为空 */
|
|
45
|
+
ids?: string[];
|
|
46
|
+
};
|
|
47
|
+
|
|
30
48
|
export type ApiCollectionUploadStartEventPayload = {
|
|
31
49
|
fileNames: string[];
|
|
32
50
|
count: number;
|
|
51
|
+
nameSpace: string | null;
|
|
33
52
|
};
|
|
34
53
|
|
|
35
54
|
export type ApiCollectionUploadSuccessEventPayload = {
|
|
@@ -39,20 +58,57 @@ export type ApiCollectionUploadSuccessEventPayload = {
|
|
|
39
58
|
apiCount: number;
|
|
40
59
|
persistLocal: boolean;
|
|
41
60
|
}>;
|
|
61
|
+
nameSpace: string | null;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type ApiCollectionChangedEventPayload = {
|
|
65
|
+
nameSpace: string | null;
|
|
66
|
+
action: "put" | "delete" | "clear";
|
|
67
|
+
ids?: string[];
|
|
42
68
|
};
|
|
43
69
|
|
|
44
70
|
export type AbuilderEventPayloadMap = {
|
|
45
71
|
[AbuilderEvents.workspaceAdd]: WorkspaceAddEventPayload;
|
|
46
72
|
[AbuilderEvents.workspaceSync]: WorkspaceSyncEventPayload;
|
|
47
73
|
[AbuilderEvents.workspaceDelete]: WorkspaceDeleteEventPayload;
|
|
74
|
+
[AbuilderEvents.blueprintLibraryChanged]: BlueprintLibraryChangedEventPayload;
|
|
48
75
|
[AbuilderEvents.apiCollectionUploadStart]: ApiCollectionUploadStartEventPayload;
|
|
49
76
|
[AbuilderEvents.apiCollectionUploadSuccess]: ApiCollectionUploadSuccessEventPayload;
|
|
77
|
+
[AbuilderEvents.apiCollectionChanged]: ApiCollectionChangedEventPayload;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export type AddEventSubscriptionOptions = {
|
|
81
|
+
/**
|
|
82
|
+
* 仅接收该 nameSpace 的事件。
|
|
83
|
+
* 省略则接收全部;传入空字符串 / null 表示默认库。
|
|
84
|
+
*/
|
|
85
|
+
nameSpace?: string | null;
|
|
50
86
|
};
|
|
51
87
|
|
|
52
88
|
type EventCallback = (payload: unknown) => void;
|
|
53
89
|
|
|
54
90
|
const listeners = new Map<string, Set<EventCallback>>();
|
|
55
91
|
|
|
92
|
+
function resolvePayloadNameSpace(payload: unknown): string | null | undefined {
|
|
93
|
+
if (!payload || typeof payload !== "object") return undefined;
|
|
94
|
+
if (!("nameSpace" in payload)) return undefined;
|
|
95
|
+
const value = (payload as { nameSpace?: unknown }).nameSpace;
|
|
96
|
+
if (value === null || value === undefined) return null;
|
|
97
|
+
if (typeof value === "string") return value;
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function matchesNameSpaceFilter(
|
|
102
|
+
payload: unknown,
|
|
103
|
+
filter: string | null | undefined
|
|
104
|
+
): boolean {
|
|
105
|
+
if (filter === undefined) return true;
|
|
106
|
+
const wanted = normalizeAppNameSpace(filter);
|
|
107
|
+
const actual = resolvePayloadNameSpace(payload);
|
|
108
|
+
if (actual === undefined) return true;
|
|
109
|
+
return normalizeAppNameSpace(actual) === wanted;
|
|
110
|
+
}
|
|
111
|
+
|
|
56
112
|
export function emitAbuilderEvent<T extends AbuilderEventName>(
|
|
57
113
|
eventName: T,
|
|
58
114
|
payload: AbuilderEventPayloadMap[T]
|
|
@@ -70,17 +126,26 @@ export function emitAbuilderEvent<T extends AbuilderEventName>(
|
|
|
70
126
|
|
|
71
127
|
export function addEventSubscription<T extends AbuilderEventName>(
|
|
72
128
|
eventName: T,
|
|
73
|
-
callback: (payload: AbuilderEventPayloadMap[T]) => void
|
|
129
|
+
callback: (payload: AbuilderEventPayloadMap[T]) => void,
|
|
130
|
+
options?: AddEventSubscriptionOptions
|
|
74
131
|
): { unsubscribe: () => void } {
|
|
75
132
|
let set = listeners.get(eventName);
|
|
76
133
|
if (!set) {
|
|
77
134
|
set = new Set();
|
|
78
135
|
listeners.set(eventName, set);
|
|
79
136
|
}
|
|
80
|
-
|
|
137
|
+
const filter =
|
|
138
|
+
options && Object.prototype.hasOwnProperty.call(options, "nameSpace")
|
|
139
|
+
? options.nameSpace
|
|
140
|
+
: undefined;
|
|
141
|
+
const wrapped: EventCallback = (payload) => {
|
|
142
|
+
if (!matchesNameSpaceFilter(payload, filter)) return;
|
|
143
|
+
callback(payload as AbuilderEventPayloadMap[T]);
|
|
144
|
+
};
|
|
145
|
+
set.add(wrapped);
|
|
81
146
|
return {
|
|
82
147
|
unsubscribe: () => {
|
|
83
|
-
set.delete(
|
|
148
|
+
set.delete(wrapped);
|
|
84
149
|
if (set.size === 0) {
|
|
85
150
|
listeners.delete(eventName);
|
|
86
151
|
}
|
|
@@ -11,6 +11,7 @@ export type WorkspaceProjectSyncMessage = {
|
|
|
11
11
|
|
|
12
12
|
export type WorkspaceProjectsChangedMessage =
|
|
13
13
|
| { type: "deleted"; projectIds: string[] }
|
|
14
|
+
| { type: "upserted"; projectIds: string[] }
|
|
14
15
|
| { type: "cleared" };
|
|
15
16
|
|
|
16
17
|
export function broadcastWorkspaceProjectsChanged(
|
|
@@ -33,7 +34,12 @@ export function subscribeWorkspaceProjectsChanged(
|
|
|
33
34
|
const channel = new BroadcastChannel(appStorageKey(LIST_CHANNEL, nameSpace));
|
|
34
35
|
channel.onmessage = (event: MessageEvent<WorkspaceProjectsChangedMessage>) => {
|
|
35
36
|
const data = event.data;
|
|
36
|
-
if (
|
|
37
|
+
if (
|
|
38
|
+
!data ||
|
|
39
|
+
(data.type !== "deleted" && data.type !== "cleared" && data.type !== "upserted")
|
|
40
|
+
) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
37
43
|
onChange(data);
|
|
38
44
|
};
|
|
39
45
|
return () => channel.close();
|
|
@@ -4,16 +4,70 @@ import {
|
|
|
4
4
|
deleteWorkspaceProject as deleteWorkspaceProjectRecord,
|
|
5
5
|
getWorkspaceProject,
|
|
6
6
|
listWorkspaceProjects,
|
|
7
|
+
putWorkspaceProject as putWorkspaceProjectRecord,
|
|
8
|
+
type WorkspaceProjectRecord,
|
|
7
9
|
} from "./workspace-project-db";
|
|
8
10
|
import {
|
|
9
11
|
clearWorkspacePreviewCaches,
|
|
10
12
|
removeWorkspacePreviewCache,
|
|
13
|
+
writeWorkspacePreviewCache,
|
|
11
14
|
} from "./workspace-project-cache";
|
|
12
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
broadcastWorkspaceProjectUpdate,
|
|
17
|
+
broadcastWorkspaceProjectsChanged,
|
|
18
|
+
} from "./workspace-project-sync";
|
|
13
19
|
import { AbuilderEvents, emitAbuilderEvent } from "./event-subscription";
|
|
14
20
|
|
|
15
21
|
export { listWorkspaceProjects, getWorkspaceProject };
|
|
16
|
-
export type { WorkspaceProjectListItem } from "./workspace-project-db";
|
|
22
|
+
export type { WorkspaceProjectListItem, WorkspaceProjectRecord } from "./workspace-project-db";
|
|
23
|
+
|
|
24
|
+
export type PutWorkspaceProjectOptions = {
|
|
25
|
+
/**
|
|
26
|
+
* 写入后是否发出工作区事件。
|
|
27
|
+
* - `false` / 省略:只写 IndexedDB(宿主自己管后端时常用)
|
|
28
|
+
* - `"add"`:发出 `workspace:add`
|
|
29
|
+
* - `"sync"`:发出 `workspace:sync`
|
|
30
|
+
*/
|
|
31
|
+
emitEvent?: false | "add" | "sync";
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function resolvedNameSpace(nameSpace?: string | null): string | null {
|
|
35
|
+
return normalizeAppNameSpace(nameSpace) || null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 写入 / 覆盖 IndexedDB 中的工作区(upsert),并更新预览缓存、通知其他标签页。
|
|
40
|
+
* `nameSpace` 需与面板的 `nameSpace` 一致。
|
|
41
|
+
*/
|
|
42
|
+
export async function putWorkspaceProject(
|
|
43
|
+
record: WorkspaceProjectRecord,
|
|
44
|
+
nameSpace?: string | null,
|
|
45
|
+
options?: PutWorkspaceProjectOptions
|
|
46
|
+
): Promise<WorkspaceProjectRecord> {
|
|
47
|
+
const saved = await putWorkspaceProjectRecord(record, nameSpace);
|
|
48
|
+
writeWorkspacePreviewCache(saved, nameSpace);
|
|
49
|
+
broadcastWorkspaceProjectUpdate(saved.id, saved.updatedAt, nameSpace);
|
|
50
|
+
broadcastWorkspaceProjectsChanged(
|
|
51
|
+
{ type: "upserted", projectIds: [saved.id] },
|
|
52
|
+
nameSpace
|
|
53
|
+
);
|
|
54
|
+
const ns = resolvedNameSpace(nameSpace);
|
|
55
|
+
if (options?.emitEvent === "add") {
|
|
56
|
+
emitAbuilderEvent(AbuilderEvents.workspaceAdd, { ...saved, nameSpace: ns });
|
|
57
|
+
} else if (options?.emitEvent === "sync") {
|
|
58
|
+
emitAbuilderEvent(AbuilderEvents.workspaceSync, { ...saved, nameSpace: ns });
|
|
59
|
+
}
|
|
60
|
+
return saved;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** `putWorkspaceProject` 的别名 */
|
|
64
|
+
export async function upsertWorkspaceProject(
|
|
65
|
+
record: WorkspaceProjectRecord,
|
|
66
|
+
nameSpace?: string | null,
|
|
67
|
+
options?: PutWorkspaceProjectOptions
|
|
68
|
+
): Promise<WorkspaceProjectRecord> {
|
|
69
|
+
return putWorkspaceProject(record, nameSpace, options);
|
|
70
|
+
}
|
|
17
71
|
|
|
18
72
|
/**
|
|
19
73
|
* 删除 IndexedDB 中保存的单个工作区(及其 localStorage 预览标记)。
|
|
@@ -28,7 +82,7 @@ export async function deleteWorkspaceProject(
|
|
|
28
82
|
broadcastWorkspaceProjectsChanged({ type: "deleted", projectIds: [id] }, nameSpace);
|
|
29
83
|
emitAbuilderEvent(AbuilderEvents.workspaceDelete, {
|
|
30
84
|
ids: [id],
|
|
31
|
-
nameSpace:
|
|
85
|
+
nameSpace: resolvedNameSpace(nameSpace),
|
|
32
86
|
});
|
|
33
87
|
}
|
|
34
88
|
|
|
@@ -44,7 +98,7 @@ export async function clearWorkspaceProjects(nameSpace?: string | null): Promise
|
|
|
44
98
|
if (items.length > 0) {
|
|
45
99
|
emitAbuilderEvent(AbuilderEvents.workspaceDelete, {
|
|
46
100
|
ids: items.map((item) => item.id),
|
|
47
|
-
nameSpace:
|
|
101
|
+
nameSpace: resolvedNameSpace(nameSpace),
|
|
48
102
|
});
|
|
49
103
|
}
|
|
50
104
|
return items.length;
|
package/src/tailwind.css
CHANGED
|
@@ -26,6 +26,70 @@
|
|
|
26
26
|
background: hsl(var(--foreground) / 0.45);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/*
|
|
30
|
+
* 物料侧栏:容器查询写在源 CSS 里(JIT 的 @[Npx] 经常未进 dist)
|
|
31
|
+
* 窄:单列、卡片最多 112px;宽:auto-fill 换行,单卡约 88–104px
|
|
32
|
+
*/
|
|
33
|
+
.rv-material-sidebar {
|
|
34
|
+
container-type: inline-size;
|
|
35
|
+
container-name: rv-material;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.rv-material-grid {
|
|
39
|
+
display: grid;
|
|
40
|
+
grid-template-columns: minmax(0, 1fr);
|
|
41
|
+
justify-items: stretch;
|
|
42
|
+
gap: 0.375rem;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.rv-material-card {
|
|
46
|
+
width: 100%;
|
|
47
|
+
max-width: 112px;
|
|
48
|
+
justify-self: center;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@container rv-material (min-width: 200px) {
|
|
52
|
+
.rv-material-grid {
|
|
53
|
+
grid-template-columns: repeat(auto-fill, minmax(88px, 104px));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.rv-material-card {
|
|
57
|
+
max-width: none;
|
|
58
|
+
justify-self: stretch;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/*
|
|
63
|
+
* 配置侧栏:窄时多列表单改单列,避免挤成一团
|
|
64
|
+
*/
|
|
65
|
+
.scope-config-sidebar {
|
|
66
|
+
container-type: inline-size;
|
|
67
|
+
container-name: rv-config;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@container rv-config (max-width: 260px) {
|
|
71
|
+
.scope-config-sidebar .grid {
|
|
72
|
+
grid-template-columns: minmax(0, 1fr) !important;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.scope-config-sidebar .grid > * {
|
|
76
|
+
min-width: 0;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
* 节点选中描边:用倒数抵消画布 scale,屏幕上始终约 2px(勿用 ring/border,会随缩放变粗)
|
|
82
|
+
*/
|
|
83
|
+
.rv-node-selected {
|
|
84
|
+
box-shadow: 0 0 0 calc(2px / var(--rv-canvas-scale, 1)) rgb(59 130 246 / 0.9);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Selecto 框选:渲染在 body,线宽保持屏幕 1px */
|
|
88
|
+
.selecto-selection {
|
|
89
|
+
border-width: 1px !important;
|
|
90
|
+
box-sizing: border-box !important;
|
|
91
|
+
}
|
|
92
|
+
|
|
29
93
|
/*
|
|
30
94
|
* Moveable 控制框:
|
|
31
95
|
* - 根节点必须高于画布元素 zIndex(节点默认 ≥1),否则多选组框线会被节点盖住,只剩锚点可见
|
|
@@ -65,14 +129,11 @@
|
|
|
65
129
|
background: transparent !important;
|
|
66
130
|
}
|
|
67
131
|
|
|
132
|
+
/* 锚点尺寸交给 Moveable 的 scale(zoom) 补偿,勿写死 px 覆盖,否则放大后会偏粗 */
|
|
68
133
|
.rv-moveable-layer .moveable-control {
|
|
69
|
-
width: 14px !important;
|
|
70
|
-
height: 14px !important;
|
|
71
|
-
margin-top: -7px !important;
|
|
72
|
-
margin-left: -7px !important;
|
|
73
134
|
border-radius: 50% !important;
|
|
74
135
|
box-sizing: border-box !important;
|
|
75
|
-
border:
|
|
136
|
+
border-color: hsl(var(--background)) !important;
|
|
76
137
|
background: #1990ff !important;
|
|
77
138
|
background: var(--moveable-color) !important;
|
|
78
139
|
z-index: 10 !important;
|