@emuanalytics/flow-rdf-core 4.0.3 → 4.0.5
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 +1 -64
- package/dist/index.d.mts +36 -31
- package/dist/index.d.ts +36 -31
- package/dist/index.mjs +20 -3249
- package/dist/lib/context/context-registry.mjs +63 -0
- package/dist/lib/context/debugger.mjs +240 -0
- package/dist/lib/context/flow-context.mjs +61 -0
- package/dist/lib/context/global-state.mjs +70 -0
- package/dist/lib/context/map-registry.mjs +59 -0
- package/dist/lib/context/query-registry.mjs +53 -0
- package/dist/lib/errors/flow-error.mjs +10 -0
- package/dist/lib/errors/flow-expression-error.mjs +14 -0
- package/dist/lib/errors/flow-map-error.mjs +9 -0
- package/dist/lib/expressions/expression-parser.mjs +198 -0
- package/dist/lib/expressions/functions/from-palette.mjs +75 -0
- package/dist/lib/expressions/functions/regex.mjs +25 -0
- package/dist/lib/expressions/functions/state.mjs +25 -0
- package/dist/lib/expressions/parser-helpers.mjs +37 -0
- package/dist/lib/expressions/reactive-expression.mjs +253 -0
- package/dist/lib/map/flow-core-map.mjs +749 -0
- package/dist/lib/map/plugins/annotations/annotation-layers.mjs +163 -0
- package/dist/lib/map/plugins/annotations/annotation.events.mjs +27 -0
- package/dist/lib/map/plugins/annotations/annotation.plugin.mjs +498 -0
- package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/modes/DirectModeOverride.mjs +67 -0
- package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/modes/DragCircleMode.mjs +65 -0
- package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/utils/create_supplementary_points_circle.mjs +15 -0
- package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/utils/drag_pan.mjs +20 -0
- package/dist/lib/map/plugins/data-join.plugin.mjs +173 -0
- package/dist/lib/map/plugins/export/export.plugin.mjs +21 -0
- package/dist/lib/map/plugins/export/map-exporter.mjs +177 -0
- package/dist/lib/map/plugins/hover-effect.plugin.mjs +58 -0
- package/dist/lib/map/plugins/index.d.mts +37 -72
- package/dist/lib/map/plugins/index.d.ts +37 -72
- package/dist/lib/map/plugins/index.mjs +11 -1811
- package/dist/lib/map/plugins/keyboard-shortcut-manager.mjs +120 -0
- package/dist/lib/map/plugins/map-plugin-base.mjs +25 -0
- package/dist/lib/map/plugins/plugin-manager.mjs +135 -0
- package/dist/lib/map/plugins/popup.plugin.mjs +55 -0
- package/dist/lib/map/plugins/selection/selection.events.mjs +21 -0
- package/dist/lib/map/plugins/selection/selection.plugin.mjs +136 -0
- package/dist/lib/map/plugins/selection/source-selection.mjs +156 -0
- package/dist/lib/map/plugins/url-sync.plugin.mjs +55 -0
- package/dist/lib/map/support/image-registry.mjs +82 -0
- package/dist/lib/map/support/map-events.mjs +71 -0
- package/dist/lib/map/support/map-feature-stream.mjs +158 -0
- package/dist/lib/map/support/map-popup.mjs +159 -0
- package/dist/lib/map/types.mjs +7 -0
- package/dist/lib/map-style/map-data-source.mjs +192 -0
- package/dist/lib/map-style/map-layer-style-builder-generated.mjs +1307 -0
- package/dist/lib/map-style/map-layer-style-builder.mjs +86 -0
- package/dist/lib/map-style/map-layer.mjs +223 -0
- package/dist/lib/map-style/map-style.mjs +178 -0
- package/dist/lib/queries/async-committer.mjs +30 -0
- package/dist/lib/queries/query.mjs +172 -0
- package/dist/lib/state-store/decorators.mjs +83 -0
- package/dist/lib/state-store/state-store.mjs +534 -0
- package/dist/lib/state-store/utils.mjs +7 -0
- package/dist/lib/utils/announce.mjs +12 -0
- package/dist/lib/utils/assert.mjs +12 -0
- package/dist/{shared/flow-rdf-core.187cb345.mjs → lib/utils/debounce.mjs} +3 -39
- package/dist/lib/utils/decorators.mjs +59 -0
- package/dist/lib/utils/deferred.mjs +11 -0
- package/dist/lib/utils/ensure-array.mjs +16 -0
- package/dist/{shared/flow-rdf-core.1f0901c5.mjs → lib/utils/feature-type-checks.mjs} +2 -2
- package/dist/lib/utils/functional.mjs +22 -0
- package/dist/lib/utils/global.mjs +18 -0
- package/dist/lib/utils/index.mjs +8 -5
- package/dist/lib/utils/random-id.mjs +9 -0
- package/dist/lib/utils/string-utils.mjs +3 -0
- package/dist/lib/utils/throttle.mjs +20 -0
- package/dist/{shared/flow-rdf-core.25e5f6d7.mjs → lib/utils/type-checks.mjs} +1 -26
- package/dist/lib/utils/watchable-promise.mjs +55 -0
- package/dist/package.json.mjs +3 -0
- package/dist/shared/{flow-rdf-core.4dde996e.d.cts → flow-rdf-core.8b5acf14.d.mts} +1315 -172
- package/dist/shared/{flow-rdf-core.4dde996e.d.ts → flow-rdf-core.8b5acf14.d.ts} +1315 -172
- package/package.json +4 -8
- package/dist/index.cjs +0 -3316
- package/dist/index.d.cts +0 -90
- package/dist/lib/map/plugins/index.cjs +0 -1851
- package/dist/lib/map/plugins/index.d.cts +0 -345
- package/dist/lib/utils/index.cjs +0 -48
- package/dist/lib/utils/index.d.cts +0 -160
- package/dist/shared/flow-rdf-core.06e85a6c.cjs +0 -1394
- package/dist/shared/flow-rdf-core.4dde996e.d.mts +0 -1916
- package/dist/shared/flow-rdf-core.55c169ca.cjs +0 -150
- package/dist/shared/flow-rdf-core.63162669.d.cts +0 -10
- package/dist/shared/flow-rdf-core.65045caf.mjs +0 -1360
- package/dist/shared/flow-rdf-core.a82b9be4.cjs +0 -34
- package/dist/shared/flow-rdf-core.f50908f0.cjs +0 -102
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as mobx from 'mobx';
|
|
2
|
+
import { toJS } from 'mobx';
|
|
3
|
+
import { createDecoratorAnnotation, createDecoratorAnnotationWithArg } from '../utils/decorators.mjs';
|
|
4
|
+
|
|
5
|
+
const SYNC_ANNOTATION_TYPE = "state-sync-annotation";
|
|
6
|
+
const REACTIVE_ANNOTATION_TYPE = "state-reactive-annotation";
|
|
7
|
+
const serializeRaw = {
|
|
8
|
+
annotationType: SYNC_ANNOTATION_TYPE,
|
|
9
|
+
type: "raw",
|
|
10
|
+
serialize(val) {
|
|
11
|
+
return toJS(val);
|
|
12
|
+
},
|
|
13
|
+
deserialize(val) {
|
|
14
|
+
return val;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const serializeString = {
|
|
18
|
+
annotationType: SYNC_ANNOTATION_TYPE,
|
|
19
|
+
type: "string",
|
|
20
|
+
serialize(val) {
|
|
21
|
+
return val.toString();
|
|
22
|
+
},
|
|
23
|
+
deserialize(val) {
|
|
24
|
+
return val;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const serializeNumber = {
|
|
28
|
+
annotationType: SYNC_ANNOTATION_TYPE,
|
|
29
|
+
type: "number",
|
|
30
|
+
serialize(val) {
|
|
31
|
+
return val.toString();
|
|
32
|
+
},
|
|
33
|
+
deserialize(val) {
|
|
34
|
+
return +val;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const serializeJson = {
|
|
38
|
+
annotationType: SYNC_ANNOTATION_TYPE,
|
|
39
|
+
type: "json",
|
|
40
|
+
serialize(val) {
|
|
41
|
+
return JSON.stringify(val);
|
|
42
|
+
},
|
|
43
|
+
deserialize(val) {
|
|
44
|
+
return JSON.parse(val);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const serializeState = {
|
|
48
|
+
annotationType: SYNC_ANNOTATION_TYPE,
|
|
49
|
+
type: "state",
|
|
50
|
+
serialize(val, group) {
|
|
51
|
+
return val.serialize("raw", group);
|
|
52
|
+
},
|
|
53
|
+
deserialize(val, target) {
|
|
54
|
+
target.deserialize("raw", val);
|
|
55
|
+
return target;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const serialize = Object.assign(createDecoratorAnnotation(serializeRaw), {
|
|
59
|
+
raw: createDecoratorAnnotation(serializeRaw),
|
|
60
|
+
string: createDecoratorAnnotation(serializeString),
|
|
61
|
+
number: createDecoratorAnnotation(serializeNumber),
|
|
62
|
+
json: createDecoratorAnnotation(serializeJson),
|
|
63
|
+
state: createDecoratorAnnotation(serializeState),
|
|
64
|
+
for: createDecoratorAnnotationWithArg(serializeRaw)
|
|
65
|
+
});
|
|
66
|
+
function createReactiveAnnotation(mobxAnnotation) {
|
|
67
|
+
return createDecoratorAnnotation({
|
|
68
|
+
annotationType: REACTIVE_ANNOTATION_TYPE,
|
|
69
|
+
mobxAnnotation
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const observable = Object.assign(createReactiveAnnotation(mobx.observable), {
|
|
73
|
+
deep: createReactiveAnnotation(mobx.observable.deep),
|
|
74
|
+
ref: createReactiveAnnotation(mobx.observable.ref),
|
|
75
|
+
struct: createReactiveAnnotation(mobx.observable.struct),
|
|
76
|
+
shallow: createReactiveAnnotation(mobx.observable.shallow)
|
|
77
|
+
});
|
|
78
|
+
const computed = Object.assign(createReactiveAnnotation(mobx.computed), {
|
|
79
|
+
struct: createReactiveAnnotation(mobx.computed.struct)
|
|
80
|
+
});
|
|
81
|
+
const action = createReactiveAnnotation(mobx.action);
|
|
82
|
+
|
|
83
|
+
export { REACTIVE_ANNOTATION_TYPE, SYNC_ANNOTATION_TYPE, action, computed, observable, serialize };
|
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
import Debug from 'debug';
|
|
2
|
+
import lzString from 'lz-string';
|
|
3
|
+
import * as mobx from 'mobx';
|
|
4
|
+
import { toStream } from 'mobx-utils';
|
|
5
|
+
import { collectStoredAnnotations } from '../utils/decorators.mjs';
|
|
6
|
+
import { randomId } from '../utils/random-id.mjs';
|
|
7
|
+
import { REACTIVE_ANNOTATION_TYPE, SYNC_ANNOTATION_TYPE } from './decorators.mjs';
|
|
8
|
+
import { ensureArray } from '../utils/ensure-array.mjs';
|
|
9
|
+
import { isDefined } from '../utils/type-checks.mjs';
|
|
10
|
+
import { FlowCoreError } from '../errors/flow-error.mjs';
|
|
11
|
+
|
|
12
|
+
var __defProp = Object.defineProperty;
|
|
13
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
14
|
+
var __publicField = (obj, key, value) => {
|
|
15
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
16
|
+
return value;
|
|
17
|
+
};
|
|
18
|
+
const debug = Debug("flow:state-store");
|
|
19
|
+
const StateStoreMarker = Symbol("FlowCoreStateStoreMarker");
|
|
20
|
+
const StateStorCache = Symbol("FlowCoreStateStorCache");
|
|
21
|
+
class StateStore {
|
|
22
|
+
/**
|
|
23
|
+
* Constructs a new `StateStore`.
|
|
24
|
+
*
|
|
25
|
+
* In derived classes, call `super()` in the constructor followed by `this.initialize(...)`.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```js
|
|
29
|
+
* constructor() {
|
|
30
|
+
* super();
|
|
31
|
+
*
|
|
32
|
+
* // Required!
|
|
33
|
+
* this.initialize();
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
* @param id - Optional unique identifier for debugging purposes. If not supplied, a random identifier will be generated.
|
|
37
|
+
*/
|
|
38
|
+
constructor(id) {
|
|
39
|
+
__publicField(this, "serializationAnnotations", {});
|
|
40
|
+
__publicField(this, "id");
|
|
41
|
+
this.id = id || randomId("state");
|
|
42
|
+
}
|
|
43
|
+
get formattedName() {
|
|
44
|
+
return `StateStore<${this.id}>`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns `true` if the store has been initialized.
|
|
48
|
+
*
|
|
49
|
+
* Derived state stores **must** be initialized before use by calling {@link StateStore.initialize}
|
|
50
|
+
* in the constructor.
|
|
51
|
+
*
|
|
52
|
+
* @returns `true` if the store has been initialized.
|
|
53
|
+
*/
|
|
54
|
+
get initialized() {
|
|
55
|
+
return mobx.isObservable(this);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Returns the value at the specified path in the state store.
|
|
59
|
+
*
|
|
60
|
+
* @param path - State store path to retrieve.
|
|
61
|
+
* @typeParam TPath - Type defining possible state store paths.
|
|
62
|
+
* @returns The value of the state store property at the specified path.
|
|
63
|
+
*/
|
|
64
|
+
get(path) {
|
|
65
|
+
this.checkInitialized();
|
|
66
|
+
const pathStr = path;
|
|
67
|
+
let value = this;
|
|
68
|
+
const p = pathStr.split(".");
|
|
69
|
+
let k;
|
|
70
|
+
while (k = p.shift()) {
|
|
71
|
+
value = value[k];
|
|
72
|
+
}
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Sets the value at the specified path in the state store.
|
|
77
|
+
*
|
|
78
|
+
* @param path - State store path to set
|
|
79
|
+
* @param val - Value to set at the specified path
|
|
80
|
+
*/
|
|
81
|
+
set(path, val) {
|
|
82
|
+
this.checkInitialized();
|
|
83
|
+
const pathArray = path.split(".");
|
|
84
|
+
let k;
|
|
85
|
+
let value = this;
|
|
86
|
+
while (pathArray.length > 1) {
|
|
87
|
+
k = pathArray.shift();
|
|
88
|
+
value = value[k];
|
|
89
|
+
}
|
|
90
|
+
k = pathArray.shift();
|
|
91
|
+
value[k] = val;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Use this method to batch multiple state updates into a single transaction.
|
|
95
|
+
* This can improve performance by reducing the number of reactive updates triggered by
|
|
96
|
+
* state updates.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* Set multiple state properties within a transaction:
|
|
100
|
+
* ```js
|
|
101
|
+
* store.transaction(() => {
|
|
102
|
+
* store.set('a', 1);
|
|
103
|
+
* store.set('b', 2);
|
|
104
|
+
* store.set('c', 3);
|
|
105
|
+
* });
|
|
106
|
+
* ```
|
|
107
|
+
*
|
|
108
|
+
* @param fn - The function to run within the transaction.
|
|
109
|
+
*/
|
|
110
|
+
transaction(fn) {
|
|
111
|
+
this.checkInitialized();
|
|
112
|
+
mobx.runInAction(() => fn(this));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Toggles the value at the specified path in the state store.
|
|
116
|
+
* This method assumes that the value at the specified path is a boolean.
|
|
117
|
+
*
|
|
118
|
+
* @param path - The path to the boolean value to toggle.
|
|
119
|
+
* @returns The new value at the specified path.
|
|
120
|
+
*/
|
|
121
|
+
toggle(path) {
|
|
122
|
+
this.checkInitialized();
|
|
123
|
+
const val = !this.get(path);
|
|
124
|
+
this.set(path, val);
|
|
125
|
+
return val;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Increments the value at the specified path in the state store.
|
|
129
|
+
* This method assumes that the value at the specified path is a number.
|
|
130
|
+
*
|
|
131
|
+
* @param path - The path to the number value to increment.
|
|
132
|
+
* @returns The new value at the specified path.
|
|
133
|
+
*/
|
|
134
|
+
increment(path) {
|
|
135
|
+
this.checkInitialized();
|
|
136
|
+
const val = +this.get(path) + 1;
|
|
137
|
+
this.set(path, val);
|
|
138
|
+
return val;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Decrements the value at the specified path in the state store.
|
|
142
|
+
* This method assumes that the value at the specified path is a number.
|
|
143
|
+
*
|
|
144
|
+
* @param path The path to the number value to decrement.
|
|
145
|
+
* @returns The new value at the specified path.
|
|
146
|
+
*/
|
|
147
|
+
decrement(path) {
|
|
148
|
+
this.checkInitialized();
|
|
149
|
+
const val = +this.get(path) - 1;
|
|
150
|
+
this.set(path, val);
|
|
151
|
+
return val;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Bind an HTML control element to a state store property. Two-way binding
|
|
155
|
+
* is established between the control and the state store property. When the
|
|
156
|
+
* control value changes, the state store property is updated. When the state
|
|
157
|
+
* store property changes, the control value is updated.
|
|
158
|
+
*
|
|
159
|
+
* Supports:
|
|
160
|
+
* - Native elements that emit an `input` event and have a `value` property.
|
|
161
|
+
* - Flo.w RDF UI custom elements that emit `flow-ui-input` or `flow-ui-change` events and have
|
|
162
|
+
* a value property.
|
|
163
|
+
*
|
|
164
|
+
* :::warning
|
|
165
|
+
* Bindings should be disposed of when no longer needed by calling `unsubscribe()` on the returned subscription.
|
|
166
|
+
* :::
|
|
167
|
+
*
|
|
168
|
+
* :::warning
|
|
169
|
+
* This method is provided to support reactive binding for vanilla JS and TS applications. When using
|
|
170
|
+
* other supported frameworks (e.g. Angular) use the preferred binding method for that framework.
|
|
171
|
+
* :::
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```js
|
|
175
|
+
* const subscription = store.bindControl(document.getElementById('my-input'), 'myStateProperty');
|
|
176
|
+
* ...
|
|
177
|
+
* subscription.unsubscribe();
|
|
178
|
+
* ```
|
|
179
|
+
*
|
|
180
|
+
* @param el - The HTML element to bind
|
|
181
|
+
* @param statePath - The path to the state store property to bind
|
|
182
|
+
* @param options - Binding options
|
|
183
|
+
* @param options.toState - A function to convert the control value to the state store value
|
|
184
|
+
* @param options.fromState - A function to convert the state store value to the control value
|
|
185
|
+
* @param options.updateOn - The event type that will trigger an update of the state store property. Default is `input`.
|
|
186
|
+
* @returns A subscription that can be used to `unsubscribe()` the binding.
|
|
187
|
+
*/
|
|
188
|
+
bindControl(el, statePath, options) {
|
|
189
|
+
const toState = options?.toState ?? ((v) => v);
|
|
190
|
+
const fromState = options?.fromState ?? ((v) => v);
|
|
191
|
+
const updateOn = options?.updateOn ?? "input";
|
|
192
|
+
el.addEventListener("input", () => {
|
|
193
|
+
if (updateOn === "input") {
|
|
194
|
+
this.set(statePath, toState(el.value));
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
el.addEventListener("flow-ui-input", () => {
|
|
198
|
+
if (updateOn === "input") {
|
|
199
|
+
this.set(statePath, toState(el.value));
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
el.addEventListener("flow-ui-change", () => {
|
|
203
|
+
if (updateOn === "change") {
|
|
204
|
+
this.set(statePath, toState(el.value));
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
return this.toStream(statePath, true).subscribe((value) => {
|
|
208
|
+
if (value !== el.value) {
|
|
209
|
+
el.value = fromState(value);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Bind an HTML element property to a state store property. One-way binding
|
|
215
|
+
* is established between the element property and the state store property.
|
|
216
|
+
* When the state store property changes, the element property is updated.
|
|
217
|
+
*
|
|
218
|
+
* :::warning
|
|
219
|
+
* Bindings should be disposed of when no longer needed by calling `unsubscribe()` on the returned subscription.
|
|
220
|
+
* :::
|
|
221
|
+
*
|
|
222
|
+
* :::warning
|
|
223
|
+
* This method is provided to support reactive binding for vanilla JS and TS applications. When using
|
|
224
|
+
* other supported frameworks (e.g. Angular) use the preferred binding method for that framework.
|
|
225
|
+
* :::
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```js
|
|
229
|
+
* const el = document.getElementById('my-span');
|
|
230
|
+
* const subscription = store.bindElementProp(el, 'textContent', 'myStateProperty');
|
|
231
|
+
* ...
|
|
232
|
+
* subscription.unsubscribe();
|
|
233
|
+
* ```
|
|
234
|
+
*
|
|
235
|
+
* @param el - The HTML element to bind
|
|
236
|
+
* @param prop - The property of the element to bind
|
|
237
|
+
* @param path - The path to the state store property to bind
|
|
238
|
+
* @param options - Binding options
|
|
239
|
+
* @param options.fromState - A function to convert the state store value to the element property value
|
|
240
|
+
* @returns A subscription that can be used to `unsubscribe()` the binding.
|
|
241
|
+
*/
|
|
242
|
+
bindElementProp(el, prop, path, options) {
|
|
243
|
+
const fromState = options?.fromState ?? ((v) => v);
|
|
244
|
+
return this.toStream(path, true).subscribe((value) => {
|
|
245
|
+
if (value !== el[prop]) {
|
|
246
|
+
el[prop] = fromState(value);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Bind an HTML element attribute to a state store property. One-way binding
|
|
252
|
+
* is established between the element attribute and the state store property.
|
|
253
|
+
* When the state store property changes, the element attribute is updated
|
|
254
|
+
* (using `el.setAttribute(...)`).
|
|
255
|
+
*
|
|
256
|
+
* :::warning
|
|
257
|
+
* Bindings should be disposed of when no longer needed by calling `unsubscribe()` on the returned subscription.
|
|
258
|
+
* :::
|
|
259
|
+
*
|
|
260
|
+
* :::warning
|
|
261
|
+
* This method is provided to support reactive binding for vanilla JS and TS applications. When using
|
|
262
|
+
* other supported frameworks (e.g. Angular) use the preferred binding method for that framework.
|
|
263
|
+
* :::
|
|
264
|
+
*
|
|
265
|
+
* @param el - The HTML element to bind
|
|
266
|
+
* @param attr - The attribute of the element to bind
|
|
267
|
+
* @param path - The path to the state store property to bind
|
|
268
|
+
* @returns
|
|
269
|
+
*/
|
|
270
|
+
bindElementAttr(el, attr, path, options) {
|
|
271
|
+
const fromState = options?.fromState ?? ((v) => v.toString ? v.toString() : v);
|
|
272
|
+
return this.toStream(path, true).subscribe((value) => {
|
|
273
|
+
el.setAttribute(attr, fromState(value));
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Create an observable stream that emits the value of the specified state store property
|
|
278
|
+
* when it changes. The stream can be subscribed to using its `subscribe()` method.
|
|
279
|
+
*
|
|
280
|
+
* :::warning
|
|
281
|
+
* Subscriptions should be disposed of when no longer required by calling their `unsubscribe()` method.
|
|
282
|
+
* :::
|
|
283
|
+
*
|
|
284
|
+
* :::danger
|
|
285
|
+
* It is an anti-pattern to modify state store properties directly from within a stream subscription.
|
|
286
|
+
* Use a `computed` property instead. Limit use of `toStream` to running imperative side-effects
|
|
287
|
+
* in response to a reactive update of a state property.
|
|
288
|
+
* :::
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* ```js
|
|
292
|
+
* const subscription = store.toStream('myStateProperty').subscribe((value) => {
|
|
293
|
+
* console.log('myStateProperty changed:', value);
|
|
294
|
+
* });
|
|
295
|
+
* ...
|
|
296
|
+
* subscription.unsubscribe();
|
|
297
|
+
* ```
|
|
298
|
+
*
|
|
299
|
+
* @param path - The path to the state store property to bind
|
|
300
|
+
* @param fireImmediately - If `true`, the stream will emit the current value immediately
|
|
301
|
+
* If `false`, the stream will only emit values when the state store property changes.
|
|
302
|
+
*
|
|
303
|
+
* @returns An observable stream that emits the value of the specified state property.
|
|
304
|
+
*/
|
|
305
|
+
toStream(path, fireImmediately = true) {
|
|
306
|
+
this.checkInitialized();
|
|
307
|
+
return toStream(() => this.get(path), fireImmediately);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Creates a reactive context that will automatically re-run the specified function
|
|
311
|
+
* whenever referenced reactive state changes. The supplied function is run immediately
|
|
312
|
+
* and referenced state properties are tracked. The function will be re-run when tracked
|
|
313
|
+
* state properties change.
|
|
314
|
+
*
|
|
315
|
+
* Note that referenced state properties are determined dynamically
|
|
316
|
+
* by access. If a code branch is not taken, the referenced state properties in that branch
|
|
317
|
+
* will not be tracked. If, on a subsequent run, the code branch is taken, the tracking
|
|
318
|
+
* will be updated to include the newly accessed state properties.
|
|
319
|
+
*
|
|
320
|
+
* This method is similar to `toStream` but allows access to the complete state object rather
|
|
321
|
+
* than a single property.
|
|
322
|
+
*
|
|
323
|
+
* :::warning
|
|
324
|
+
* Autoruns should be disposed of when no longer needed by invoking the `disposer` function
|
|
325
|
+
* returned when creating an autorun.
|
|
326
|
+
* :::
|
|
327
|
+
*
|
|
328
|
+
* :::danger
|
|
329
|
+
* It is an anti-pattern to modify state store properties directly from within an autorun.
|
|
330
|
+
* Use a `computed` property instead. Limit use of autoruns to running imperative side-effects
|
|
331
|
+
* in response to a reactive update.
|
|
332
|
+
* :::
|
|
333
|
+
*
|
|
334
|
+
* @example
|
|
335
|
+
* ```js
|
|
336
|
+
* const disposer = store.autorun((state) => {
|
|
337
|
+
* // Set map properties as a side effect whenever any of `state.zoom`, `state.bearing`, or `state.pitch` change
|
|
338
|
+
* map.setCenter({zoom: state.zoom, bearing: state.bearing, pitch: state.pitch});
|
|
339
|
+
* });
|
|
340
|
+
* ...
|
|
341
|
+
* disposer();
|
|
342
|
+
* ```
|
|
343
|
+
*
|
|
344
|
+
* @param autorunFn - The function to run within a reactive context
|
|
345
|
+
* @param name - Optional name for debugging purposes
|
|
346
|
+
* @returns - A disposer function. Invoke this function to dispose of the autorun.
|
|
347
|
+
*/
|
|
348
|
+
autorun(autorunFn, name) {
|
|
349
|
+
this.checkInitialized();
|
|
350
|
+
return mobx.autorun(() => autorunFn(this), { name });
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* This method **must**
|
|
354
|
+
* be called in the constructor of derived classes to initialize the state store.
|
|
355
|
+
* An exception will be thrown if this method is not called before using the state store.
|
|
356
|
+
*
|
|
357
|
+
* If using decorators to configure reactive properties, this method should be called without arguments.
|
|
358
|
+
* If not using decorators, supply reactive annotations to configure reactive properties.
|
|
359
|
+
*
|
|
360
|
+
* See {@link StoreAnnotations} for a list of available annotations.
|
|
361
|
+
*
|
|
362
|
+
* :::info
|
|
363
|
+
* If this method is called without using decorators and without supplying reactive annotations,
|
|
364
|
+
* the state store will be configured automatically:
|
|
365
|
+
*
|
|
366
|
+
* - All properties will be made observable.
|
|
367
|
+
* - All getters will converted to computed properties.
|
|
368
|
+
*
|
|
369
|
+
* This is only recommended for simple state stores in demos.
|
|
370
|
+
* :::
|
|
371
|
+
*
|
|
372
|
+
* @param annotations Optional annotations for configuring reactive properties.
|
|
373
|
+
*/
|
|
374
|
+
initialize(annotations = {}) {
|
|
375
|
+
if (this.initialized) {
|
|
376
|
+
throw new FlowCoreError(`${this.formattedName}: store has already been initialized`);
|
|
377
|
+
}
|
|
378
|
+
const annotationsByType = {
|
|
379
|
+
[REACTIVE_ANNOTATION_TYPE]: collectStoredAnnotations(this, REACTIVE_ANNOTATION_TYPE),
|
|
380
|
+
[SYNC_ANNOTATION_TYPE]: collectStoredAnnotations(this, SYNC_ANNOTATION_TYPE)
|
|
381
|
+
};
|
|
382
|
+
Object.keys(annotations).forEach((prop) => {
|
|
383
|
+
const propAnnotations = ensureArray(annotations[prop]);
|
|
384
|
+
propAnnotations.forEach((annotation) => {
|
|
385
|
+
const type = annotation.annotationType;
|
|
386
|
+
annotationsByType[type] ?? (annotationsByType[type] = {});
|
|
387
|
+
annotationsByType[type][prop] = annotation;
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
this.serializationAnnotations = annotationsByType[SYNC_ANNOTATION_TYPE];
|
|
391
|
+
if (Object.keys(annotationsByType[REACTIVE_ANNOTATION_TYPE]).length) {
|
|
392
|
+
const mobxAnnotations = Object.keys(annotationsByType[REACTIVE_ANNOTATION_TYPE]).reduce((accum, prop) => {
|
|
393
|
+
accum[prop] = annotationsByType[REACTIVE_ANNOTATION_TYPE][prop].mobxAnnotation;
|
|
394
|
+
return accum;
|
|
395
|
+
}, {});
|
|
396
|
+
const reactivity = Object.keys(mobxAnnotations).reduce((accum, prop) => {
|
|
397
|
+
accum[prop] = mobxAnnotations[prop].annotationType_;
|
|
398
|
+
return accum;
|
|
399
|
+
}, {});
|
|
400
|
+
const serialization = Object.keys(annotationsByType[SYNC_ANNOTATION_TYPE]).reduce((accum, prop) => {
|
|
401
|
+
accum[prop] = annotationsByType[SYNC_ANNOTATION_TYPE][prop].syncType;
|
|
402
|
+
return accum;
|
|
403
|
+
}, {});
|
|
404
|
+
this.makeObservable(mobxAnnotations);
|
|
405
|
+
debug(`${this.formattedName}: initialized with annotations`, {
|
|
406
|
+
reactivity,
|
|
407
|
+
serialization
|
|
408
|
+
});
|
|
409
|
+
} else {
|
|
410
|
+
this.makeAutoObservable();
|
|
411
|
+
debug(`${this.formattedName}: initialized with auto-annotations`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Serialize the state store properties that have been annotated as serializable to an object or string.
|
|
416
|
+
*
|
|
417
|
+
* The following formats are supported:
|
|
418
|
+
* - `raw` (default): Plain object with property values serialized as JSON strings.
|
|
419
|
+
* - `json`: The `raw` object format serialized as a single JSON string.
|
|
420
|
+
* - `lz`: The `raw` object format serialized as a single JSON string and then compressed using [lzString.compressToEncodedURIComponent](https://github.com/pieroxy/lz-string).
|
|
421
|
+
*
|
|
422
|
+
* The `lz` format is designed for serializing state store properties to a URL query string.
|
|
423
|
+
*/
|
|
424
|
+
serialize(format = "raw", group) {
|
|
425
|
+
const annotations = this.serializationAnnotations;
|
|
426
|
+
const serializedProps = Object.keys(annotations).reduce((accum, prop) => {
|
|
427
|
+
const annotationGroup = ensureArray(annotations[prop].arg);
|
|
428
|
+
const annotationType = annotations[prop].type;
|
|
429
|
+
if (group && annotationType !== "state" && !annotationGroup.includes(group)) {
|
|
430
|
+
return accum;
|
|
431
|
+
}
|
|
432
|
+
const val = this[prop];
|
|
433
|
+
if (isDefined(val)) {
|
|
434
|
+
const serializedValue = annotations[prop].serialize(val, group);
|
|
435
|
+
accum[prop] = serializedValue;
|
|
436
|
+
}
|
|
437
|
+
return accum;
|
|
438
|
+
}, {});
|
|
439
|
+
switch (format) {
|
|
440
|
+
case "json":
|
|
441
|
+
return JSON.stringify(serializedProps);
|
|
442
|
+
case "lz":
|
|
443
|
+
return lzString.compressToEncodedURIComponent(JSON.stringify(serializedProps));
|
|
444
|
+
default:
|
|
445
|
+
return serializedProps;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Set state store properties by deserializing from serialized output of the {@link serialize} method.
|
|
450
|
+
* Only properties that have been annotated as serializable and are present in the serialized value will be deserialized.
|
|
451
|
+
* Other properties will be left unchanged.
|
|
452
|
+
*/
|
|
453
|
+
deserialize(value, format = "raw") {
|
|
454
|
+
let deserializedValue;
|
|
455
|
+
switch (format) {
|
|
456
|
+
case "json":
|
|
457
|
+
deserializedValue = JSON.parse(value);
|
|
458
|
+
break;
|
|
459
|
+
case "lz":
|
|
460
|
+
deserializedValue = JSON.parse(lzString.decompressFromEncodedURIComponent(value));
|
|
461
|
+
break;
|
|
462
|
+
default:
|
|
463
|
+
deserializedValue = value;
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
this.transaction(() => {
|
|
467
|
+
const annotations = this.serializationAnnotations;
|
|
468
|
+
Object.keys(deserializedValue).forEach((prop) => {
|
|
469
|
+
const annotation = annotations[prop];
|
|
470
|
+
if (annotation) {
|
|
471
|
+
this[prop] = annotation.deserialize(deserializedValue[prop], this[prop]);
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
makeObservable(overrides) {
|
|
477
|
+
if (this.initialized) {
|
|
478
|
+
throw new FlowCoreError(`${this.formattedName}: store is already observable`);
|
|
479
|
+
}
|
|
480
|
+
overrides = {
|
|
481
|
+
...overrides,
|
|
482
|
+
set: mobx.action,
|
|
483
|
+
toggle: mobx.action,
|
|
484
|
+
increment: mobx.action,
|
|
485
|
+
decrement: mobx.action
|
|
486
|
+
};
|
|
487
|
+
mobx.makeObservable(this, overrides, { name: this.formattedName });
|
|
488
|
+
}
|
|
489
|
+
makeAutoObservable(overrides = {}) {
|
|
490
|
+
if (this.initialized) {
|
|
491
|
+
throw new FlowCoreError(`${this.formattedName}: store is already observable`);
|
|
492
|
+
}
|
|
493
|
+
const target = this;
|
|
494
|
+
overrides = {
|
|
495
|
+
...overrides,
|
|
496
|
+
_initialized: false,
|
|
497
|
+
$mobx: false,
|
|
498
|
+
constructor: false
|
|
499
|
+
};
|
|
500
|
+
let annotations = target[StateStorCache];
|
|
501
|
+
if (!annotations) {
|
|
502
|
+
annotations = {};
|
|
503
|
+
let current = target;
|
|
504
|
+
while (current && !Object.prototype.hasOwnProperty.call(current, StateStoreMarker)) {
|
|
505
|
+
Reflect.ownKeys(current).forEach((k) => {
|
|
506
|
+
annotations[k] = !overrides ? true : k in overrides ? overrides[k] : true;
|
|
507
|
+
});
|
|
508
|
+
current = Object.getPrototypeOf(current);
|
|
509
|
+
}
|
|
510
|
+
const proto = Object.getPrototypeOf(target);
|
|
511
|
+
if (proto) {
|
|
512
|
+
Object.defineProperty(proto, StateStorCache, { value: annotations });
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
annotations = {
|
|
516
|
+
...annotations,
|
|
517
|
+
set: mobx.action,
|
|
518
|
+
toggle: mobx.action,
|
|
519
|
+
increment: mobx.action,
|
|
520
|
+
decrement: mobx.action
|
|
521
|
+
};
|
|
522
|
+
return mobx.makeObservable(target, annotations, { name: this.formattedName });
|
|
523
|
+
}
|
|
524
|
+
checkInitialized() {
|
|
525
|
+
if (!this.initialized) {
|
|
526
|
+
throw new FlowCoreError(
|
|
527
|
+
`${this.formattedName} not initialized. Please use one of the recommended methods to make this state store observable.`
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
StateStore.prototype[StateStoreMarker] = true;
|
|
533
|
+
|
|
534
|
+
export { StateStore };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { version } from '../../package.json.mjs';
|
|
2
|
+
|
|
3
|
+
const CONSOLE_HIGHLIGHT_STYLE = `
|
|
4
|
+
padding: 2px 4px;
|
|
5
|
+
border-radius: 2px;
|
|
6
|
+
background: #389DFF;
|
|
7
|
+
color: white;`;
|
|
8
|
+
function announce() {
|
|
9
|
+
console.log("%cFlo.w RDF Core v%s", CONSOLE_HIGHLIGHT_STYLE, version);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { announce as default };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FlowCoreError } from '../errors/flow-error.mjs';
|
|
2
|
+
|
|
3
|
+
function assert(condition, message) {
|
|
4
|
+
if (!condition) {
|
|
5
|
+
throw new FlowCoreError(message ?? "Assertion failed");
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
function die(message) {
|
|
9
|
+
throw new FlowCoreError(message);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { assert, die };
|
|
@@ -1,25 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
const doNotMemoize = Symbol("Do not memoize");
|
|
4
|
-
function memoize(originalFunction, options) {
|
|
5
|
-
const cache = /* @__PURE__ */ new Map();
|
|
6
|
-
const hash = options.hash ? options.hash : (...args) => args[0];
|
|
7
|
-
const context = options.context;
|
|
8
|
-
return function memoizedFunction(...args) {
|
|
9
|
-
const key = hash.apply(context, args);
|
|
10
|
-
if (key === doNotMemoize) {
|
|
11
|
-
return originalFunction.apply(context, args);
|
|
12
|
-
}
|
|
13
|
-
let value;
|
|
14
|
-
if (!cache.has(key)) {
|
|
15
|
-
value = originalFunction.apply(context, args);
|
|
16
|
-
cache.set(key, value);
|
|
17
|
-
} else {
|
|
18
|
-
value = cache.get(key);
|
|
19
|
-
}
|
|
20
|
-
return value;
|
|
21
|
-
};
|
|
22
|
-
}
|
|
1
|
+
import { memoize } from './functional.mjs';
|
|
2
|
+
import { isNumber } from './type-checks.mjs';
|
|
23
3
|
|
|
24
4
|
function debounce(func, wait, options) {
|
|
25
5
|
options = {
|
|
@@ -125,20 +105,4 @@ function debounceWithArgs(fn, wait, options) {
|
|
|
125
105
|
};
|
|
126
106
|
}
|
|
127
107
|
|
|
128
|
-
|
|
129
|
-
if (typeof func != "function") {
|
|
130
|
-
throw new TypeError("Expected a function");
|
|
131
|
-
}
|
|
132
|
-
return debounce(func, wait, { ...options, maxWait: wait });
|
|
133
|
-
}
|
|
134
|
-
function throttleWithArgs(fn, wait, options) {
|
|
135
|
-
const memoizedThrottleFn = memoize(
|
|
136
|
-
() => debounce(fn, wait, options),
|
|
137
|
-
options
|
|
138
|
-
);
|
|
139
|
-
return (...args) => {
|
|
140
|
-
memoizedThrottleFn(...args)(...args);
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export { debounceWithArgs as a, throttleWithArgs as b, debounce as d, memoize as m, throttle as t };
|
|
108
|
+
export { debounce, debounceWithArgs, debounce as default };
|