@netless/window-manager 1.0.0-canary.52 → 1.0.0-canary.54
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/index.cjs.js +389 -445
- package/dist/index.es.js +434 -490
- package/dist/index.umd.js +389 -446
- package/dist/src/App/AppContext.d.ts +8 -8
- package/dist/src/AppManager.d.ts +5 -1
- package/dist/src/Cursor/index.d.ts +1 -0
- package/dist/src/Utils/Reactive.d.ts +1 -1
- package/dist/src/View/CameraSynchronizer.d.ts +4 -3
- package/dist/src/View/ScrollMode.d.ts +32 -0
- package/dist/src/View/ViewSync.d.ts +4 -2
- package/dist/src/callback.d.ts +3 -0
- package/dist/src/constants.d.ts +2 -0
- package/dist/src/index.d.ts +21 -11
- package/dist/src/storage.d.ts +7 -0
- package/dist/src/typings.d.ts +5 -4
- package/dist/style.css +2 -1
- package/docs/api.md +10 -0
- package/package.json +4 -3
- package/pnpm-lock.yaml +28 -73
- package/src/App/AppContext.ts +19 -8
- package/src/App/WhiteboardView.ts +4 -2
- package/src/AppListener.ts +1 -10
- package/src/AppManager.ts +19 -1
- package/src/Cursor/index.ts +6 -2
- package/src/Utils/Reactive.ts +2 -1
- package/src/View/CameraSynchronizer.ts +35 -23
- package/src/View/MainView.ts +1 -0
- package/src/View/ScrollMode.ts +229 -0
- package/src/View/ViewSync.ts +31 -18
- package/src/callback.ts +3 -0
- package/src/constants.ts +3 -0
- package/src/index.ts +56 -63
- package/src/storage.ts +15 -0
- package/src/style.css +1 -1
- package/src/typings.ts +6 -3
- package/vite.config.js +1 -1
- package/dist/src/App/Storage/StorageEvent.d.ts +0 -8
- package/dist/src/App/Storage/index.d.ts +0 -39
- package/dist/src/App/Storage/typings.d.ts +0 -22
- package/dist/src/App/Storage/utils.d.ts +0 -5
- package/src/App/Storage/StorageEvent.ts +0 -21
- package/src/App/Storage/index.ts +0 -295
- package/src/App/Storage/typings.ts +0 -23
- package/src/App/Storage/utils.ts +0 -17
package/src/App/Storage/index.ts
DELETED
@@ -1,295 +0,0 @@
|
|
1
|
-
import type { AkkoObjectUpdatedProperty } from "white-web-sdk";
|
2
|
-
import { get, has, mapValues, isObject, size, noop } from "lodash";
|
3
|
-
import { SideEffectManager } from "side-effect-manager";
|
4
|
-
import type { AppContext } from "../AppContext";
|
5
|
-
import { safeListenPropsUpdated } from "../../Utils/Reactive";
|
6
|
-
import { isRef, makeRef, plainObjectKeys } from "./utils";
|
7
|
-
import type { Diff, MaybeRefValue, RefValue, StorageStateChangedEvent, StorageStateChangedListener, StorageStateChangedListenerDisposer } from "./typings";
|
8
|
-
import { StorageEvent } from "./StorageEvent";
|
9
|
-
|
10
|
-
export * from './typings';
|
11
|
-
|
12
|
-
export const STORAGE_NS = "_WM-STORAGE_";
|
13
|
-
|
14
|
-
export class Storage<TState extends Record<string, any> = any> implements Storage<TState> {
|
15
|
-
readonly id: string | null;
|
16
|
-
|
17
|
-
private readonly _context: AppContext;
|
18
|
-
private readonly _sideEffect = new SideEffectManager();
|
19
|
-
private _state: TState;
|
20
|
-
private _destroyed = false;
|
21
|
-
|
22
|
-
private _refMap = new WeakMap<any, RefValue>();
|
23
|
-
|
24
|
-
/**
|
25
|
-
* `setState` alters local state immediately before sending to server. This will cache the old value for onStateChanged diffing.
|
26
|
-
*/
|
27
|
-
private _lastValue = new Map<string | number | symbol, TState[Extract<keyof TState, string>]>();
|
28
|
-
|
29
|
-
constructor(context: AppContext, id?: string, defaultState?: TState) {
|
30
|
-
if (defaultState && !isObject(defaultState)) {
|
31
|
-
throw new Error(`Default state for Storage ${id} is not an object.`);
|
32
|
-
}
|
33
|
-
|
34
|
-
this._context = context;
|
35
|
-
this.id = id || null;
|
36
|
-
|
37
|
-
this._state = {} as TState;
|
38
|
-
const rawState = this._getRawState(this._state);
|
39
|
-
|
40
|
-
if (this._context.isWritable) {
|
41
|
-
if (this.id === null) {
|
42
|
-
if (context.isAddApp && defaultState) {
|
43
|
-
this.setState(defaultState);
|
44
|
-
}
|
45
|
-
} else {
|
46
|
-
if (rawState === this._state || !isObject(rawState)) {
|
47
|
-
if (!get(this._context.getAttributes(), [STORAGE_NS])) {
|
48
|
-
this._context.updateAttributes([STORAGE_NS], {});
|
49
|
-
}
|
50
|
-
this._context.updateAttributes([STORAGE_NS, this.id], this._state);
|
51
|
-
if (defaultState) {
|
52
|
-
this.setState(defaultState);
|
53
|
-
}
|
54
|
-
}
|
55
|
-
}
|
56
|
-
}
|
57
|
-
|
58
|
-
// strip mobx
|
59
|
-
plainObjectKeys(rawState).forEach(key => {
|
60
|
-
if (this.id === null && key === STORAGE_NS) {
|
61
|
-
return;
|
62
|
-
}
|
63
|
-
try {
|
64
|
-
const rawValue = isObject(rawState[key]) ? JSON.parse(JSON.stringify(rawState[key])) : rawState[key];
|
65
|
-
if (isRef<TState[Extract<keyof TState, string>]>(rawValue)) {
|
66
|
-
this._state[key] = rawValue.v;
|
67
|
-
if (isObject(rawValue.v)) {
|
68
|
-
this._refMap.set(rawValue.v, rawValue);
|
69
|
-
}
|
70
|
-
} else {
|
71
|
-
this._state[key] = rawValue;
|
72
|
-
}
|
73
|
-
} catch (e) {
|
74
|
-
console.error(e);
|
75
|
-
}
|
76
|
-
});
|
77
|
-
|
78
|
-
this._sideEffect.addDisposer(
|
79
|
-
safeListenPropsUpdated(
|
80
|
-
() => this.id === null ? context.getAttributes() : get(context.getAttributes(), [STORAGE_NS, this.id]),
|
81
|
-
this._updateProperties.bind(this),
|
82
|
-
this.destroy.bind(this)
|
83
|
-
)
|
84
|
-
);
|
85
|
-
}
|
86
|
-
|
87
|
-
get state(): Readonly<TState> {
|
88
|
-
if (this._destroyed) {
|
89
|
-
console.warn(`Accessing state on destroyed Storage "${this.id}"`)
|
90
|
-
}
|
91
|
-
return this._state;
|
92
|
-
}
|
93
|
-
|
94
|
-
readonly onStateChanged = new StorageEvent<StorageStateChangedEvent<TState>>();
|
95
|
-
|
96
|
-
addStateChangedListener(handler: StorageStateChangedListener<TState>): StorageStateChangedListenerDisposer {
|
97
|
-
this.onStateChanged.addListener(handler);
|
98
|
-
return () => this.onStateChanged.removeListener(handler);
|
99
|
-
}
|
100
|
-
|
101
|
-
ensureState(state: Partial<TState>): void {
|
102
|
-
return this.setState(
|
103
|
-
plainObjectKeys(state).reduce((payload, key) => {
|
104
|
-
if (!has(this._state, key)) {
|
105
|
-
payload[key] = state[key];
|
106
|
-
}
|
107
|
-
return payload;
|
108
|
-
}, {} as Partial<TState>)
|
109
|
-
);
|
110
|
-
}
|
111
|
-
|
112
|
-
setState(state: Partial<TState>): void {
|
113
|
-
if (this._destroyed) {
|
114
|
-
console.error(new Error(`Cannot call setState on destroyed Storage "${this.id}".`));
|
115
|
-
return;
|
116
|
-
}
|
117
|
-
|
118
|
-
if (!this._context.isWritable) {
|
119
|
-
console.error(new Error(`Cannot setState on Storage "${this.id}" without writable access`), state);
|
120
|
-
return;
|
121
|
-
}
|
122
|
-
|
123
|
-
const keys = plainObjectKeys(state);
|
124
|
-
if (keys.length > 0) {
|
125
|
-
keys.forEach(key => {
|
126
|
-
const value = state[key];
|
127
|
-
if (value === this._state[key]) {
|
128
|
-
return;
|
129
|
-
}
|
130
|
-
|
131
|
-
if (value === void 0) {
|
132
|
-
this._lastValue.set(key, this._state[key]);
|
133
|
-
delete this._state[key];
|
134
|
-
this._setRawState(key, value);
|
135
|
-
} else {
|
136
|
-
this._lastValue.set(key, this._state[key]);
|
137
|
-
this._state[key] = value as TState[Extract<keyof TState, string>];
|
138
|
-
|
139
|
-
let payload: MaybeRefValue<typeof value> = value;
|
140
|
-
if (isObject(value)) {
|
141
|
-
let refValue = this._refMap.get(value);
|
142
|
-
if (!refValue) {
|
143
|
-
refValue = makeRef(value);
|
144
|
-
this._refMap.set(value, refValue);
|
145
|
-
}
|
146
|
-
payload = refValue;
|
147
|
-
}
|
148
|
-
|
149
|
-
this._setRawState(key, payload)
|
150
|
-
}
|
151
|
-
});
|
152
|
-
}
|
153
|
-
}
|
154
|
-
|
155
|
-
/**
|
156
|
-
* Empty storage data.
|
157
|
-
*/
|
158
|
-
emptyStorage(): void {
|
159
|
-
if (size(this._state) <= 0) {
|
160
|
-
return;
|
161
|
-
}
|
162
|
-
|
163
|
-
if (this._destroyed) {
|
164
|
-
console.error(new Error(`Cannot empty destroyed Storage "${this.id}".`));
|
165
|
-
return;
|
166
|
-
}
|
167
|
-
|
168
|
-
if (!this._context.isWritable) {
|
169
|
-
console.error(new Error(`Cannot empty Storage "${this.id}" without writable access.`));
|
170
|
-
return;
|
171
|
-
}
|
172
|
-
|
173
|
-
this.setState(mapValues(this._state, noop as () => undefined));
|
174
|
-
}
|
175
|
-
|
176
|
-
/**
|
177
|
-
* Delete storage index with all of its data and destroy the Storage instance.
|
178
|
-
*/
|
179
|
-
deleteStorage(): void {
|
180
|
-
if (this.id === null) {
|
181
|
-
throw new Error(`Cannot delete main Storage`);
|
182
|
-
}
|
183
|
-
|
184
|
-
if (!this._context.isWritable) {
|
185
|
-
console.error(new Error(`Cannot delete Storage "${this.id}" without writable access.`));
|
186
|
-
return;
|
187
|
-
}
|
188
|
-
|
189
|
-
this.destroy();
|
190
|
-
|
191
|
-
this._context.updateAttributes([STORAGE_NS, this.id], void 0);
|
192
|
-
}
|
193
|
-
|
194
|
-
get destroyed(): boolean {
|
195
|
-
return this._destroyed;
|
196
|
-
}
|
197
|
-
|
198
|
-
/**
|
199
|
-
* Destroy the Storage instance. The data will be kept.
|
200
|
-
*/
|
201
|
-
destroy() {
|
202
|
-
this._destroyed = true;
|
203
|
-
this._sideEffect.flushAll();
|
204
|
-
}
|
205
|
-
|
206
|
-
private _getRawState(): TState | undefined
|
207
|
-
private _getRawState(defaultValue: TState): TState
|
208
|
-
private _getRawState(defaultValue?: TState): TState | undefined {
|
209
|
-
if (this.id === null) {
|
210
|
-
return this._context.getAttributes() ?? defaultValue;
|
211
|
-
} else {
|
212
|
-
return get(this._context.getAttributes(), [STORAGE_NS, this.id], defaultValue);
|
213
|
-
}
|
214
|
-
}
|
215
|
-
|
216
|
-
private _setRawState(key: string, value: any): void {
|
217
|
-
if (this.id === null) {
|
218
|
-
if (key === STORAGE_NS) {
|
219
|
-
throw new Error(`Cannot set attribute internal filed "${STORAGE_NS}"`)
|
220
|
-
}
|
221
|
-
return this._context.updateAttributes([key], value);
|
222
|
-
} else {
|
223
|
-
return this._context.updateAttributes([STORAGE_NS, this.id, key], value);
|
224
|
-
}
|
225
|
-
}
|
226
|
-
|
227
|
-
private _updateProperties(actions: ReadonlyArray<AkkoObjectUpdatedProperty<TState, string>>): void {
|
228
|
-
if (this._destroyed) {
|
229
|
-
console.error(new Error(`Cannot call _updateProperties on destroyed Storage "${this.id}".`));
|
230
|
-
return;
|
231
|
-
}
|
232
|
-
|
233
|
-
if (actions.length > 0) {
|
234
|
-
const diffs: Diff<TState> = {};
|
235
|
-
|
236
|
-
for (let i = 0; i < actions.length; i++) {
|
237
|
-
try {
|
238
|
-
const action = actions[i]
|
239
|
-
const key = action.key as Extract<keyof TState, string>;
|
240
|
-
|
241
|
-
if (this.id === null && key === STORAGE_NS) {
|
242
|
-
continue
|
243
|
-
}
|
244
|
-
|
245
|
-
const value = isObject(action.value) ? JSON.parse(JSON.stringify(action.value)) : action.value;
|
246
|
-
let oldValue: TState[Extract<keyof TState, string>] | undefined;
|
247
|
-
if (this._lastValue.has(key)) {
|
248
|
-
oldValue = this._lastValue.get(key);
|
249
|
-
this._lastValue.delete(key);
|
250
|
-
}
|
251
|
-
|
252
|
-
switch (action.kind) {
|
253
|
-
case 2: {
|
254
|
-
// Removed
|
255
|
-
if (has(this._state, key)) {
|
256
|
-
oldValue = this._state[key];
|
257
|
-
delete this._state[key];
|
258
|
-
}
|
259
|
-
diffs[key] = { oldValue };
|
260
|
-
break;
|
261
|
-
}
|
262
|
-
default: {
|
263
|
-
let newValue = value;
|
264
|
-
|
265
|
-
if (isRef<TState[Extract<keyof TState, string>]>(value)) {
|
266
|
-
const { k, v } = value;
|
267
|
-
const curValue = this._state[key];
|
268
|
-
if (isObject(curValue) && this._refMap.get(curValue)?.k === k) {
|
269
|
-
newValue = curValue;
|
270
|
-
} else {
|
271
|
-
newValue = v;
|
272
|
-
if (isObject(v)) {
|
273
|
-
this._refMap.set(v, value);
|
274
|
-
}
|
275
|
-
}
|
276
|
-
}
|
277
|
-
|
278
|
-
if (newValue !== this._state[key]) {
|
279
|
-
oldValue = this._state[key];
|
280
|
-
this._state[key] = newValue;
|
281
|
-
}
|
282
|
-
|
283
|
-
diffs[key] = { newValue, oldValue };
|
284
|
-
break;
|
285
|
-
}
|
286
|
-
}
|
287
|
-
} catch (e) {
|
288
|
-
console.error(e)
|
289
|
-
}
|
290
|
-
}
|
291
|
-
|
292
|
-
this.onStateChanged.dispatch(diffs);
|
293
|
-
}
|
294
|
-
}
|
295
|
-
}
|
@@ -1,23 +0,0 @@
|
|
1
|
-
import type { StorageEventListener } from "./StorageEvent";
|
2
|
-
|
3
|
-
export type RefValue<TValue = any> = { k: string; v: TValue; __isRef: true };
|
4
|
-
|
5
|
-
export type ExtractRawValue<TValue> = TValue extends RefValue<infer TRefValue> ? TRefValue : TValue;
|
6
|
-
|
7
|
-
export type AutoRefValue<TValue> = RefValue<ExtractRawValue<TValue>>;
|
8
|
-
|
9
|
-
export type MaybeRefValue<TValue> = TValue | AutoRefValue<TValue>;
|
10
|
-
|
11
|
-
export type DiffOne<T> = { oldValue?: T; newValue?: T };
|
12
|
-
|
13
|
-
export type Diff<T> = { [K in keyof T]?: DiffOne<T[K]> };
|
14
|
-
|
15
|
-
export type StorageOnSetStatePayload<TState = unknown> = {
|
16
|
-
[K in keyof TState]?: MaybeRefValue<TState[K]>;
|
17
|
-
};
|
18
|
-
|
19
|
-
export type StorageStateChangedEvent<TState = any> = Diff<TState>;
|
20
|
-
|
21
|
-
export type StorageStateChangedListener<TState = any> = StorageEventListener<StorageStateChangedEvent<TState>>;
|
22
|
-
|
23
|
-
export type StorageStateChangedListenerDisposer = () => void;
|
package/src/App/Storage/utils.ts
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
import { has } from "lodash";
|
2
|
-
import { genUID } from "side-effect-manager";
|
3
|
-
import type { AutoRefValue, ExtractRawValue, RefValue } from "./typings";
|
4
|
-
|
5
|
-
export const plainObjectKeys = Object.keys as <T>(o: T) => Array<Extract<keyof T, string>>;
|
6
|
-
|
7
|
-
export function isRef<TValue = unknown>(e: unknown): e is RefValue<TValue> {
|
8
|
-
return Boolean(has(e, '__isRef'));
|
9
|
-
}
|
10
|
-
|
11
|
-
export function makeRef<TValue>(v: TValue): RefValue<TValue> {
|
12
|
-
return { k: genUID(), v, __isRef: true };
|
13
|
-
}
|
14
|
-
|
15
|
-
export function makeAutoRef<TValue>(v: TValue): AutoRefValue<TValue> {
|
16
|
-
return isRef<ExtractRawValue<TValue>>(v) ? v : makeRef(v as ExtractRawValue<TValue>);
|
17
|
-
}
|