@arronqzy/react-view 1.0.21 → 1.0.23
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/package.json +11 -11
- package/readme.md +6 -1
- package/src/panel/I18nRoot.tsx +14 -2
- package/src/panel/ReactViewOnlinePreview.tsx +29 -18
- package/src/panel/ReactViewPanel.tsx +154 -1006
- package/src/panel/components/PanelConfigSidebar.tsx +297 -3712
- package/src/panel/components/PanelHistoryPopover.tsx +163 -0
- package/src/panel/components/PanelLayerDock.tsx +412 -0
- package/src/panel/components/PanelMenubar.tsx +149 -0
- package/src/panel/components/PanelWorkspaceDialogs.tsx +271 -0
- package/src/panel/components/config/PanelConfigAudioSection.tsx +334 -0
- package/src/panel/components/config/PanelConfigChartSection.tsx +799 -0
- package/src/panel/components/config/PanelConfigGeometrySection.tsx +233 -0
- package/src/panel/components/config/PanelConfigGridChildSpan.tsx +70 -0
- package/src/panel/components/config/PanelConfigGridSection.tsx +118 -0
- package/src/panel/components/config/PanelConfigMultiSelect.tsx +1291 -0
- package/src/panel/components/config/PanelConfigNodeInfo.tsx +169 -0
- package/src/panel/components/config/PanelConfigReferenceSection.tsx +99 -0
- package/src/panel/components/config/PanelConfigScene3dSection.tsx +6 -2
- package/src/panel/components/config/PanelConfigStyleSections.tsx +249 -0
- package/src/panel/components/config/PanelConfigTextSection.tsx +268 -0
- package/src/panel/components/config/PanelConfigVideoSection.tsx +133 -0
- package/src/panel/components/config/PanelConfigViewportSection.tsx +139 -0
- package/src/panel/components/config/helpers.ts +85 -0
- package/src/panel/hooks/useWorkspaceProjects.ts +112 -34
- package/src/panel/icons.tsx +160 -0
- package/src/panel/index.tsx +15 -1
- package/src/panel/library/parse-workspace-data.ts +142 -0
- package/src/panel/library/workspace-project-cache.ts +13 -4
- package/src/panel/library/workspace-project-db.ts +24 -13
- package/src/panel/library/workspace-project-sync.ts +17 -6
- package/src/panel/utils/outputScale.ts +9 -4
- package/src/panel/utils/panelSelection.ts +57 -0
- package/src/panel/utils/panelStateIO.ts +18 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BlueprintDocument } from "@arronqzy/react-blueprint";
|
|
2
2
|
import type { BlueprintMetaDraft } from "@arronqzy/react-blueprint";
|
|
3
3
|
import type { State } from "@arronqzy/rx-store";
|
|
4
|
+
import { appStorageKey } from "@arronqzy/blueprint-dsl";
|
|
4
5
|
|
|
5
6
|
const DB_NAME = "arronqzy-workspace-projects";
|
|
6
7
|
const DB_VERSION = 1;
|
|
@@ -25,9 +26,9 @@ export type WorkspaceProjectRecord = {
|
|
|
25
26
|
titleIconDataUrl?: string;
|
|
26
27
|
};
|
|
27
28
|
|
|
28
|
-
function openDb(): Promise<IDBDatabase> {
|
|
29
|
+
function openDb(nameSpace?: string | null): Promise<IDBDatabase> {
|
|
29
30
|
return new Promise((resolve, reject) => {
|
|
30
|
-
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
31
|
+
const request = indexedDB.open(appStorageKey(DB_NAME, nameSpace), DB_VERSION);
|
|
31
32
|
request.onerror = () => reject(request.error ?? new Error("indexeddb-open-failed"));
|
|
32
33
|
request.onupgradeneeded = () => {
|
|
33
34
|
const db = request.result;
|
|
@@ -42,9 +43,10 @@ function openDb(): Promise<IDBDatabase> {
|
|
|
42
43
|
|
|
43
44
|
function runTransaction<T>(
|
|
44
45
|
mode: IDBTransactionMode,
|
|
45
|
-
runner: (store: IDBObjectStore) => IDBRequest<T
|
|
46
|
+
runner: (store: IDBObjectStore) => IDBRequest<T>,
|
|
47
|
+
nameSpace?: string | null
|
|
46
48
|
): Promise<T> {
|
|
47
|
-
return openDb().then(
|
|
49
|
+
return openDb(nameSpace).then(
|
|
48
50
|
(db) =>
|
|
49
51
|
new Promise<T>((resolve, reject) => {
|
|
50
52
|
const tx = db.transaction(STORE_NAME, mode);
|
|
@@ -77,8 +79,10 @@ export function createWorkspaceProjectId(): string {
|
|
|
77
79
|
return `ws-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
export async function listWorkspaceProjects(
|
|
81
|
-
|
|
82
|
+
export async function listWorkspaceProjects(
|
|
83
|
+
nameSpace?: string | null
|
|
84
|
+
): Promise<WorkspaceProjectListItem[]> {
|
|
85
|
+
const db = await openDb(nameSpace);
|
|
82
86
|
return new Promise((resolve, reject) => {
|
|
83
87
|
const tx = db.transaction(STORE_NAME, "readonly");
|
|
84
88
|
const store = tx.objectStore(STORE_NAME);
|
|
@@ -110,21 +114,28 @@ export async function listWorkspaceProjects(): Promise<WorkspaceProjectListItem[
|
|
|
110
114
|
}
|
|
111
115
|
|
|
112
116
|
export async function getWorkspaceProject(
|
|
113
|
-
id: string
|
|
117
|
+
id: string,
|
|
118
|
+
nameSpace?: string | null
|
|
114
119
|
): Promise<WorkspaceProjectRecord | null> {
|
|
115
|
-
const record = await runTransaction<WorkspaceProjectRecord | undefined>(
|
|
116
|
-
|
|
120
|
+
const record = await runTransaction<WorkspaceProjectRecord | undefined>(
|
|
121
|
+
"readonly",
|
|
122
|
+
(store) => store.get(id),
|
|
123
|
+
nameSpace
|
|
117
124
|
);
|
|
118
125
|
return record ?? null;
|
|
119
126
|
}
|
|
120
127
|
|
|
121
128
|
export async function putWorkspaceProject(
|
|
122
|
-
record: WorkspaceProjectRecord
|
|
129
|
+
record: WorkspaceProjectRecord,
|
|
130
|
+
nameSpace?: string | null
|
|
123
131
|
): Promise<WorkspaceProjectRecord> {
|
|
124
|
-
await runTransaction<IDBValidKey>("readwrite", (store) => store.put(record));
|
|
132
|
+
await runTransaction<IDBValidKey>("readwrite", (store) => store.put(record), nameSpace);
|
|
125
133
|
return record;
|
|
126
134
|
}
|
|
127
135
|
|
|
128
|
-
export async function deleteWorkspaceProject(
|
|
129
|
-
|
|
136
|
+
export async function deleteWorkspaceProject(
|
|
137
|
+
id: string,
|
|
138
|
+
nameSpace?: string | null
|
|
139
|
+
): Promise<void> {
|
|
140
|
+
await runTransaction<undefined>("readwrite", (store) => store.delete(id), nameSpace);
|
|
130
141
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { appStorageKey } from "@arronqzy/blueprint-dsl";
|
|
2
|
+
|
|
1
3
|
const CHANNEL_PREFIX = "arronqzy-workspace-project";
|
|
2
4
|
|
|
3
5
|
export type WorkspaceProjectSyncMessage = {
|
|
@@ -6,16 +8,22 @@ export type WorkspaceProjectSyncMessage = {
|
|
|
6
8
|
updatedAt: number;
|
|
7
9
|
};
|
|
8
10
|
|
|
9
|
-
export function getWorkspaceProjectChannelName(
|
|
10
|
-
|
|
11
|
+
export function getWorkspaceProjectChannelName(
|
|
12
|
+
projectId: string,
|
|
13
|
+
nameSpace?: string | null
|
|
14
|
+
): string {
|
|
15
|
+
return appStorageKey(`${CHANNEL_PREFIX}:${projectId}`, nameSpace);
|
|
11
16
|
}
|
|
12
17
|
|
|
13
18
|
export function broadcastWorkspaceProjectUpdate(
|
|
14
19
|
projectId: string,
|
|
15
|
-
updatedAt: number
|
|
20
|
+
updatedAt: number,
|
|
21
|
+
nameSpace?: string | null
|
|
16
22
|
): void {
|
|
17
23
|
if (typeof BroadcastChannel === "undefined") return;
|
|
18
|
-
const channel = new BroadcastChannel(
|
|
24
|
+
const channel = new BroadcastChannel(
|
|
25
|
+
getWorkspaceProjectChannelName(projectId, nameSpace)
|
|
26
|
+
);
|
|
19
27
|
channel.postMessage({
|
|
20
28
|
type: "updated",
|
|
21
29
|
projectId,
|
|
@@ -26,12 +34,15 @@ export function broadcastWorkspaceProjectUpdate(
|
|
|
26
34
|
|
|
27
35
|
export function subscribeWorkspaceProjectUpdates(
|
|
28
36
|
projectId: string,
|
|
29
|
-
onUpdate: (message: WorkspaceProjectSyncMessage) => void
|
|
37
|
+
onUpdate: (message: WorkspaceProjectSyncMessage) => void,
|
|
38
|
+
nameSpace?: string | null
|
|
30
39
|
): () => void {
|
|
31
40
|
if (typeof BroadcastChannel === "undefined") {
|
|
32
41
|
return () => {};
|
|
33
42
|
}
|
|
34
|
-
const channel = new BroadcastChannel(
|
|
43
|
+
const channel = new BroadcastChannel(
|
|
44
|
+
getWorkspaceProjectChannelName(projectId, nameSpace)
|
|
45
|
+
);
|
|
35
46
|
channel.onmessage = (event: MessageEvent<WorkspaceProjectSyncMessage>) => {
|
|
36
47
|
const data = event.data;
|
|
37
48
|
if (!data || data.type !== "updated" || data.projectId !== projectId) return;
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/** 产物预览是否缩放内部内容(字体等)。默认 false:块仍按视口撑满,内容保持原尺寸以免失真。 */
|
|
2
|
+
import { appStorageKey } from "@arronqzy/blueprint-dsl";
|
|
3
|
+
|
|
2
4
|
export const OUTPUT_SCALE_STORAGE_KEY = "panel:outputScale";
|
|
3
5
|
|
|
4
|
-
export function readOutputScale(): boolean {
|
|
6
|
+
export function readOutputScale(nameSpace?: string | null): boolean {
|
|
5
7
|
try {
|
|
6
|
-
return localStorage.getItem(OUTPUT_SCALE_STORAGE_KEY) === "1";
|
|
8
|
+
return localStorage.getItem(appStorageKey(OUTPUT_SCALE_STORAGE_KEY, nameSpace)) === "1";
|
|
7
9
|
} catch {
|
|
8
10
|
return false;
|
|
9
11
|
}
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
export function writeOutputScale(enabled: boolean) {
|
|
14
|
+
export function writeOutputScale(enabled: boolean, nameSpace?: string | null) {
|
|
13
15
|
try {
|
|
14
|
-
localStorage.setItem(
|
|
16
|
+
localStorage.setItem(
|
|
17
|
+
appStorageKey(OUTPUT_SCALE_STORAGE_KEY, nameSpace),
|
|
18
|
+
enabled ? "1" : "0"
|
|
19
|
+
);
|
|
15
20
|
} catch {
|
|
16
21
|
// ignore storage errors
|
|
17
22
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export function formatRelativeTime(
|
|
2
|
+
timestamp: number,
|
|
3
|
+
now: number,
|
|
4
|
+
t: (key: string, params?: Record<string, string | number>) => string
|
|
5
|
+
): string {
|
|
6
|
+
const diff = Math.max(0, now - timestamp);
|
|
7
|
+
const sec = Math.floor(diff / 1000);
|
|
8
|
+
if (sec < 60) return t("common.secondsAgo", { sec });
|
|
9
|
+
const min = Math.floor(sec / 60);
|
|
10
|
+
if (min < 60) return t("common.minutesAgo", { min });
|
|
11
|
+
const hour = Math.floor(min / 60);
|
|
12
|
+
if (hour < 24) return t("common.hoursAgo", { hour });
|
|
13
|
+
const day = Math.floor(hour / 24);
|
|
14
|
+
return t("common.daysAgo", { day });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getSelectedTargetsFromIds(
|
|
18
|
+
container: HTMLElement | null,
|
|
19
|
+
ids: string[],
|
|
20
|
+
) {
|
|
21
|
+
if (!container) return [];
|
|
22
|
+
const targets: HTMLElement[] = [];
|
|
23
|
+
for (const id of ids) {
|
|
24
|
+
const el = container.querySelector<HTMLElement>(
|
|
25
|
+
`[data-element-id="${id}"]`,
|
|
26
|
+
);
|
|
27
|
+
if (el) targets.push(el);
|
|
28
|
+
}
|
|
29
|
+
return targets;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function shouldClearSelectionOnBlank(target: HTMLElement | null) {
|
|
33
|
+
if (!target) return false;
|
|
34
|
+
if (
|
|
35
|
+
target.closest(".rv-selectable") ||
|
|
36
|
+
target.closest(".moveable-control-box") ||
|
|
37
|
+
target.closest(".moveable-group") ||
|
|
38
|
+
target.closest(".moveable-line") ||
|
|
39
|
+
target.closest(".moveable-control") ||
|
|
40
|
+
target.closest(".moveable-direction")
|
|
41
|
+
) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (
|
|
45
|
+
target.closest("button") ||
|
|
46
|
+
target.closest("input") ||
|
|
47
|
+
target.closest("select") ||
|
|
48
|
+
target.closest("textarea") ||
|
|
49
|
+
target.closest("label") ||
|
|
50
|
+
target.closest("a") ||
|
|
51
|
+
target.closest("[role='menuitem']") ||
|
|
52
|
+
target.closest("[data-radix-popper-content-wrapper]")
|
|
53
|
+
) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
@@ -26,6 +26,24 @@ export function normalizeImportedPanelState(state: unknown): State | null {
|
|
|
26
26
|
return next;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
export function createEmptyPanelState(): State {
|
|
30
|
+
return {
|
|
31
|
+
root: {
|
|
32
|
+
id: "root",
|
|
33
|
+
type: "root",
|
|
34
|
+
props: {},
|
|
35
|
+
children: [],
|
|
36
|
+
},
|
|
37
|
+
selectedIds: [],
|
|
38
|
+
variables: {
|
|
39
|
+
counter: 0,
|
|
40
|
+
isOpen: false,
|
|
41
|
+
layers: [DEFAULT_LAYER],
|
|
42
|
+
activeLayerId: DEFAULT_LAYER_ID,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
29
47
|
export function parseAllPanelElements(state: State): PanelElement[] {
|
|
30
48
|
return (state.root.children ?? [])
|
|
31
49
|
.filter((node) => {
|