@woosh/meep-engine 2.37.16 → 2.37.19
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/core/assert.js +16 -2
- package/core/collection/array/typed/isTypedArray.js +20 -0
- package/core/collection/array/typedArrayToDataType.js +2 -1
- package/core/collection/table/RowFirstTable.js +34 -0
- package/core/collection/table/RowFirstTable.spec.js +59 -1
- package/core/color/Color.js +14 -0
- package/core/color/rgb2hex.js +1 -1
- package/core/events/signal/Signal.js +8 -8
- package/core/events/signal/Signal.spec.js +16 -0
- package/core/geom/Quaternion.d.ts +5 -0
- package/core/geom/Quaternion.js +152 -137
- package/core/geom/Quaternion.spec.js +47 -2
- package/core/geom/Vector3.schema.json +16 -0
- package/core/json/JsonUtils.js +2 -20
- package/core/model/ObservedEnum.js +8 -0
- package/editor/Editor.js +97 -1
- package/editor/actions/concrete/PatchTerrainHeightAction.js +1 -1
- package/editor/ecs/component/FieldDescriptor.js +34 -0
- package/editor/ecs/component/FieldValueAdapter.js +20 -0
- package/editor/ecs/component/TypeEditor.js +33 -0
- package/editor/ecs/component/TypeSchema.d.ts +38 -0
- package/editor/ecs/component/createFieldEditor.js +90 -0
- package/editor/ecs/component/createObjectEditor.js +242 -60
- package/editor/ecs/component/editors/ColorEditor.js +39 -0
- package/editor/ecs/component/editors/HTMLElementEditor.js +17 -0
- package/editor/ecs/component/editors/ImagePathEditor.js +50 -0
- package/editor/ecs/component/editors/NumericIntervalEditor.js +86 -0
- package/editor/ecs/component/editors/ObservedBooleanEditor.js +13 -0
- package/editor/ecs/component/editors/ObservedEnumEditor.js +32 -0
- package/editor/ecs/component/editors/ObservedIntegerEditor.js +43 -0
- package/editor/ecs/component/editors/ObservedStringEditor.js +51 -0
- package/editor/ecs/component/editors/Sampler2DEditor.js +60 -0
- package/editor/ecs/component/editors/collection/ListEditor.js +83 -0
- package/editor/ecs/component/editors/common/BitFlagsEditor.js +80 -0
- package/editor/ecs/component/editors/common/EnumEditor.js +41 -0
- package/editor/ecs/component/editors/common/makeV3_editor.js +85 -0
- package/editor/ecs/component/editors/common/noEditor.js +9 -0
- package/editor/ecs/component/editors/ecs/GridObstacleEditor.js +17 -0
- package/editor/ecs/component/editors/ecs/MinimapMarkerEditor.js +16 -0
- package/editor/ecs/component/editors/ecs/ParameterLookupTableEditor.js +44 -0
- package/editor/ecs/component/editors/ecs/ParameterTrackEditor.js +17 -0
- package/editor/ecs/component/editors/ecs/ParticleEmitterEditor.js +58 -0
- package/editor/ecs/component/editors/ecs/ParticleEmitterLayerEditor.js +54 -0
- package/editor/ecs/component/editors/ecs/SimulationStepDefinitionEditor.js +21 -0
- package/editor/ecs/component/editors/ecs/Trail2DEditor.js +33 -0
- package/editor/ecs/component/editors/ecs/TransformEditor.js +23 -0
- package/editor/ecs/component/editors/ecs/terrain/SplatMappingEditor.js +21 -0
- package/editor/ecs/component/editors/ecs/terrain/TerrainEditor.js +42 -0
- package/editor/ecs/component/editors/ecs/terrain/TerrainLayerEditor.js +18 -0
- package/editor/ecs/component/editors/ecs/terrain/TerrainLayersEditor.js +22 -0
- package/editor/ecs/component/editors/ecs/terrain/TerrainOverlayEditor.js +20 -0
- package/editor/ecs/component/editors/geom/QuaternionEditor.js +56 -0
- package/editor/ecs/component/editors/geom/Vector1Editor.js +57 -0
- package/editor/ecs/component/editors/geom/Vector2Editor.js +11 -0
- package/editor/ecs/component/editors/geom/Vector3Editor.js +13 -0
- package/editor/ecs/component/editors/geom/Vector4Editor.js +12 -0
- package/editor/ecs/component/editors/primitive/ArrayEditor.js +46 -0
- package/editor/ecs/component/editors/primitive/BooleanEditor.js +27 -0
- package/editor/ecs/component/editors/primitive/FunctionEditor.js +25 -0
- package/editor/ecs/component/editors/primitive/NumberEditor.js +60 -0
- package/editor/ecs/component/editors/primitive/ObjectEditor.js +12 -0
- package/editor/ecs/component/editors/primitive/StringEditor.js +31 -0
- package/editor/ecs/component/editors/three/BufferGeometryEditor.js +28 -0
- package/editor/ecs/component/editors/three/MaterialEditor.js +27 -0
- package/editor/ecs/component/editors/three/MeshEditor.js +35 -0
- package/editor/ecs/component/editors/three/TextureEditor.js +32 -0
- package/editor/ecs/component/findNearestRegisteredType.js +59 -0
- package/editor/ecs/component/prototypeObjectEditor.js +379 -0
- package/editor/view/EditorView.js +1 -1
- package/editor/view/ecs/ComponentControlView.js +2 -30
- package/editor/view/ecs/EntityEditor.js +61 -139
- package/editor/view/ecs/components/GridObstacleController.js +4 -4
- package/editor/view/ecs/components/TerrainController.js +1 -1
- package/editor/view/ecs/components/common/NumberController.js +19 -7
- package/engine/animation/keyed2/AnimationTrack.js +1 -1
- package/engine/asset/AssetManager.js +1 -1
- package/engine/ecs/EntityBlueprint.js +1 -1
- package/engine/ecs/EntityBuilder.js +2 -2
- package/engine/ecs/EntityManager.js +1 -1
- package/engine/ecs/components/TagEditor.js +15 -0
- package/engine/ecs/parent/EntityNode.js +3 -0
- package/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter.js +1 -1
- package/engine/ecs/terrain/ecs/Terrain.js +23 -42
- package/engine/ecs/terrain/ecs/TerrainSystem.js +2 -2
- package/engine/ecs/terrain/ecs/layers/TerrainLayer.js +1 -1
- package/engine/ecs/terrain/util/loadVisibleTerrainTiles.js +1 -1
- package/engine/ecs/transform/Transform.editor.schema.json +16 -0
- package/engine/ecs/transform/Transform.js +3 -0
- package/engine/ecs/util/hideEntityGracefully.js +4 -2
- package/engine/graphics/ecs/highlight/HighlightEditor.js +17 -0
- package/engine/graphics/ecs/mesh/MeshEditor.js +28 -0
- package/engine/graphics/ecs/mesh-v2/aggregate/prototypeSGMesh.js +3 -3
- package/engine/graphics/micron/render/instanced/shader/shader_rewrite_standard.js +15 -15
- package/engine/graphics/particles/particular/engine/ParticularEngine.js +5 -0
- package/engine/graphics/texture/sampler/Sampler2D.js +16 -0
- package/engine/graphics/util/ScaleObject3ToBox.js +14 -1
- package/engine/graphics/util/makeMeshPreviewScene.js +2 -1
- package/engine/grid/components/GridObstacle.js +0 -44
- package/engine/grid/components/GridObstacleSerializationAdapter.js +46 -0
- package/engine/knowledge/database/StaticKnowledgeDatabase.js +1 -1
- package/engine/navigation/ecs/components/Path.d.ts +2 -0
- package/engine/navigation/ecs/components/Path.js +6 -1
- package/engine/sound/material/concrete/json/serializeSoundMaterialToJSON.js +1 -1
- package/generation/example/SampleGenerator0.js +33 -29
- package/generation/example/generators/interactive/mir_generator_place_buff_objects.js +7 -6
- package/generation/example/generators/mir_generator_place_bases.js +7 -3
- package/generation/example/generators/mir_generator_place_road_decorators.js +3 -3
- package/generation/example/generators/mir_generator_place_starting_point.js +3 -2
- package/generation/markers/GridCellActionPlaceMarker.js +9 -3
- package/generation/markers/transform/MarkerNodeTransformerOffsetPosition.js +1 -1
- package/generation/placement/GridCellPlacementRule.js +22 -23
- package/generation/theme/ThemeEngine.js +1 -1
- package/package.json +1 -1
- package/samples/terrain/main.js +1 -1
- package/view/View.js +23 -1
- package/view/common/LabelView.js +1 -1
- package/view/compose3x3transform.js +32 -8
- package/view/controller/dat/DatGuiUtils.js +1 -1
- package/view/elements/DropDownSelectionView.js +11 -3
- package/view/elements/image/ImageView.js +3 -1
- package/core/model/ObservedReal.js +0 -55
- package/editor/ecs/component/ObjectEditor.js +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @template T
|
|
3
|
+
*/
|
|
4
|
+
export class FieldDescriptor {
|
|
5
|
+
constructor() {
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @type {string}
|
|
9
|
+
*/
|
|
10
|
+
this.name = "";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* typeof field
|
|
14
|
+
* @type {Class<T>}
|
|
15
|
+
*/
|
|
16
|
+
this.type = null;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @type {FieldValueAdapter|null}
|
|
21
|
+
*/
|
|
22
|
+
this.adapter = null;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
*
|
|
26
|
+
* @type {{}|undefined}
|
|
27
|
+
*/
|
|
28
|
+
this.schema = undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
toString() {
|
|
32
|
+
return `FieldDescriptor[name='${this.name}']`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { assert } from "../../../core/assert.js";
|
|
2
|
+
|
|
3
|
+
export class FieldValueAdapter {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.writable = true;
|
|
6
|
+
this.readable = true;
|
|
7
|
+
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
read(object, field_name) {
|
|
11
|
+
assert.isString(field_name, 'field_name');
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
return object[field_name];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
write(object, field_name, value) {
|
|
18
|
+
object[field_name] = value;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @class
|
|
3
|
+
* @template T
|
|
4
|
+
*/
|
|
5
|
+
export class TypeEditor {
|
|
6
|
+
inline = true;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Class is hidden and editor should not be displayed
|
|
10
|
+
* @returns {boolean}
|
|
11
|
+
*/
|
|
12
|
+
get hidden() {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @returns {TypeSchema|undefined}
|
|
19
|
+
*/
|
|
20
|
+
get schema() {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {*} parent
|
|
26
|
+
* @param {FieldDescriptor} field
|
|
27
|
+
* @param {Map<*, TypeEditor>} registry
|
|
28
|
+
* @returns {View|void}
|
|
29
|
+
*/
|
|
30
|
+
build(parent, field, registry) {
|
|
31
|
+
throw new Error('Not Implemented');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {DataType} from "../../../core/parser/simple/DataType";
|
|
2
|
+
|
|
3
|
+
interface Type<T> extends Function {
|
|
4
|
+
new(...args: any[]): T;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface PropertiesSchema {
|
|
8
|
+
[key: string]: FieldSchema<any>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface TypeSchema {
|
|
12
|
+
additionalProperties?: boolean
|
|
13
|
+
properties: PropertiesSchema
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface TypeEditor {
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface FieldSchema<T> {
|
|
21
|
+
type: Type<T>
|
|
22
|
+
type_parameters?: any[]
|
|
23
|
+
transient?: boolean
|
|
24
|
+
description?: string,
|
|
25
|
+
editor?: TypeEditor
|
|
26
|
+
deprecated?: boolean,
|
|
27
|
+
enum?: T[],
|
|
28
|
+
numeric_type: DataType,
|
|
29
|
+
/**
|
|
30
|
+
* Small image to be displayed next to field name
|
|
31
|
+
*/
|
|
32
|
+
thumbnail: string,
|
|
33
|
+
/**
|
|
34
|
+
* @see https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-00#section-6.2.2
|
|
35
|
+
*/
|
|
36
|
+
maximum?: number,
|
|
37
|
+
minimum?: number
|
|
38
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import EmptyView from "../../../view/elements/EmptyView.js";
|
|
2
|
+
import LabelView from "../../../view/common/LabelView.js";
|
|
3
|
+
|
|
4
|
+
function format_field_name(name) {
|
|
5
|
+
return name
|
|
6
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2') // break up camel case
|
|
7
|
+
.replace(/([^.])_([^.])/g, '$1 $2') // break up underscore case
|
|
8
|
+
.replace(/(^\w{1})|(\s+\w{1})/g, letter => letter.toUpperCase()); // capitalize every word
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @template CTX
|
|
13
|
+
* @param {Object} object
|
|
14
|
+
* @param {FieldDescriptor} field
|
|
15
|
+
* @param {Map<*, TypeEditor>} registry
|
|
16
|
+
*/
|
|
17
|
+
export function createFieldEditor(object, field, registry) {
|
|
18
|
+
|
|
19
|
+
const field_schema = field.schema;
|
|
20
|
+
|
|
21
|
+
let vValue;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @type {TypeEditor}
|
|
25
|
+
*/
|
|
26
|
+
let factory;
|
|
27
|
+
if (field_schema !== undefined) {
|
|
28
|
+
const specific_field_editor = field_schema.editor;
|
|
29
|
+
|
|
30
|
+
if (specific_field_editor !== undefined) {
|
|
31
|
+
// field has a dedicated editor
|
|
32
|
+
factory = specific_field_editor;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (factory === undefined) {
|
|
37
|
+
factory = registry.get(field.type);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let orientation_inline = true;
|
|
41
|
+
|
|
42
|
+
if (factory !== undefined) {
|
|
43
|
+
if (factory.hidden) {
|
|
44
|
+
// hidden class
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
vValue = factory.build(object, field, registry);
|
|
49
|
+
|
|
50
|
+
if (vValue) {
|
|
51
|
+
orientation_inline = factory.inline;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (vValue === null || vValue === undefined) {
|
|
56
|
+
// no view
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const vResult = new EmptyView({
|
|
61
|
+
classList: ['auto-field-editor']
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (orientation_inline) {
|
|
65
|
+
vResult.addClass('orientation-inline');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const field_formatted_name = format_field_name(field.name);
|
|
69
|
+
|
|
70
|
+
const vLabel = new LabelView(field_formatted_name, { classList: ['field-name'] });
|
|
71
|
+
|
|
72
|
+
vResult.addChild(vLabel);
|
|
73
|
+
vResult.addChild(vValue);
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
if (field_schema !== undefined) {
|
|
77
|
+
if (field_schema.description !== undefined && field_schema.description.length > 0) {
|
|
78
|
+
// add tooltip
|
|
79
|
+
vLabel.attr({
|
|
80
|
+
title: field_schema.description
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (field_schema.deprecated) {
|
|
85
|
+
vResult.addClass('field-deprecated');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return vResult;
|
|
90
|
+
}
|
|
@@ -1,52 +1,30 @@
|
|
|
1
1
|
import EmptyView from "../../../view/elements/EmptyView.js";
|
|
2
|
+
import { FieldDescriptor } from "./FieldDescriptor.js";
|
|
3
|
+
import { FieldValueAdapter } from "./FieldValueAdapter.js";
|
|
4
|
+
import { createFieldEditor } from "./createFieldEditor.js";
|
|
5
|
+
import { findNearestRegisteredType } from "./findNearestRegisteredType.js";
|
|
2
6
|
import LabelView from "../../../view/common/LabelView.js";
|
|
3
7
|
|
|
4
|
-
class FieldValueAdapter {
|
|
5
|
-
read(object, field_name) {
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
write(object, field_name, value) {
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
8
|
/**
|
|
15
9
|
* @template T
|
|
10
|
+
* @param {T|undefined} value
|
|
16
11
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
*
|
|
21
|
-
* @type {string}
|
|
22
|
-
*/
|
|
23
|
-
this.name = "";
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* typeof field
|
|
27
|
-
* @type {Class<T>}
|
|
28
|
-
*/
|
|
29
|
-
this.type = null;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
*
|
|
33
|
-
* @type {FieldValueAdapter|null}
|
|
34
|
-
*/
|
|
35
|
-
this.adapter = null;
|
|
36
|
-
|
|
12
|
+
function objectToClass(value) {
|
|
13
|
+
if (value === null || value === undefined) {
|
|
14
|
+
return;
|
|
37
15
|
}
|
|
38
|
-
}
|
|
39
16
|
|
|
40
|
-
/**
|
|
41
|
-
* @template T
|
|
42
|
-
* @param {T} value
|
|
43
|
-
*/
|
|
44
|
-
function objectToClass(value) {
|
|
45
17
|
const type = typeof value;
|
|
46
18
|
|
|
47
19
|
switch (type) {
|
|
48
20
|
case "object":
|
|
49
|
-
|
|
21
|
+
const proto = Object.getPrototypeOf(value);
|
|
22
|
+
|
|
23
|
+
if (proto === null) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return proto.constructor;
|
|
50
28
|
case "boolean":
|
|
51
29
|
return Boolean;
|
|
52
30
|
case "function":
|
|
@@ -78,10 +56,24 @@ function isPublicField(object, field_name) {
|
|
|
78
56
|
/**
|
|
79
57
|
*
|
|
80
58
|
* @param {Object} object
|
|
81
|
-
* @
|
|
59
|
+
* @param {string} field_name
|
|
60
|
+
* @returns {boolean}
|
|
82
61
|
*/
|
|
83
|
-
function
|
|
84
|
-
|
|
62
|
+
function isReservedField(object, field_name) {
|
|
63
|
+
if (field_name.startsWith('@')) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
*
|
|
72
|
+
* @param {Object} object
|
|
73
|
+
* @param {FieldDescriptor[]} result
|
|
74
|
+
* @param {*} object_proto
|
|
75
|
+
*/
|
|
76
|
+
function extract_object_fields_reflection(object, result, object_proto) {
|
|
85
77
|
const keys = Object.keys(object);
|
|
86
78
|
|
|
87
79
|
for (let i = 0; i < keys.length; i++) {
|
|
@@ -91,6 +83,10 @@ function extractObjectFields(object) {
|
|
|
91
83
|
continue;
|
|
92
84
|
}
|
|
93
85
|
|
|
86
|
+
if (isReservedField(object, key)) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
|
|
94
90
|
const field_type = objectToClass(object[key]);
|
|
95
91
|
|
|
96
92
|
if (field_type === undefined) {
|
|
@@ -100,56 +96,242 @@ function extractObjectFields(object) {
|
|
|
100
96
|
const descriptor = new FieldDescriptor();
|
|
101
97
|
descriptor.name = key;
|
|
102
98
|
descriptor.type = field_type;
|
|
99
|
+
descriptor.adapter = new FieldValueAdapter();
|
|
103
100
|
|
|
104
101
|
result.push(descriptor);
|
|
105
102
|
}
|
|
106
103
|
|
|
107
|
-
|
|
104
|
+
const descriptors = Object.getOwnPropertyDescriptors(object_proto);
|
|
105
|
+
|
|
106
|
+
for (let d_name in descriptors) {
|
|
107
|
+
const d = descriptors[d_name];
|
|
108
|
+
|
|
109
|
+
const adapter = new FieldValueAdapter();
|
|
110
|
+
|
|
111
|
+
if (d.get === undefined) {
|
|
112
|
+
adapter.readable = false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (d.set === undefined) {
|
|
116
|
+
adapter.writable = false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!adapter.readable) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
let field_value;
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
field_value = adapter.read(object, d_name);
|
|
128
|
+
} catch (e) {
|
|
129
|
+
console.warn(e);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const field_type = objectToClass(field_value);
|
|
134
|
+
|
|
135
|
+
if (field_type === undefined) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const descriptor = new FieldDescriptor();
|
|
140
|
+
|
|
141
|
+
descriptor.name = d_name;
|
|
142
|
+
descriptor.type = field_type;
|
|
143
|
+
descriptor.adapter = adapter;
|
|
144
|
+
|
|
145
|
+
result.push(descriptor);
|
|
146
|
+
}
|
|
108
147
|
}
|
|
109
148
|
|
|
110
149
|
/**
|
|
111
150
|
*
|
|
112
151
|
* @param {Object} object
|
|
113
|
-
* @param {
|
|
114
|
-
* @
|
|
152
|
+
* @param {Map<*, TypeEditor>} registry
|
|
153
|
+
* @returns {FieldDescriptor[]}
|
|
115
154
|
*/
|
|
116
|
-
|
|
117
|
-
const
|
|
155
|
+
function extractObjectFields(object, registry) {
|
|
156
|
+
const result = [];
|
|
118
157
|
|
|
119
|
-
|
|
158
|
+
const object_proto = Object.getPrototypeOf(object);
|
|
159
|
+
const Klass = object_proto.constructor;
|
|
120
160
|
|
|
121
|
-
|
|
161
|
+
const editor = registry.get(Klass);
|
|
122
162
|
|
|
123
|
-
|
|
163
|
+
if (!(editor?.schema?.additionalProperties === false)) {
|
|
164
|
+
|
|
165
|
+
extract_object_fields_reflection(object, result, object_proto);
|
|
124
166
|
|
|
125
|
-
if (factory !== undefined) {
|
|
126
|
-
vValue = factory(object, field);
|
|
127
|
-
} else {
|
|
128
|
-
vValue = createObjectEditor(object, registry);
|
|
129
167
|
}
|
|
130
168
|
|
|
131
|
-
vResult.addChild(vValue);
|
|
132
169
|
|
|
133
|
-
|
|
170
|
+
if (editor !== undefined) {
|
|
171
|
+
|
|
172
|
+
const schema = editor.schema;
|
|
173
|
+
|
|
174
|
+
if (schema !== undefined) {
|
|
175
|
+
// has schema, apply
|
|
176
|
+
const prop_keys = Object.keys(schema.properties);
|
|
177
|
+
|
|
178
|
+
for (let i = 0; i < prop_keys.length; i++) {
|
|
179
|
+
const key = prop_keys[i];
|
|
180
|
+
|
|
181
|
+
const property = schema.properties[key];
|
|
182
|
+
|
|
183
|
+
const field_index = result.findIndex(a => a.name === key);
|
|
184
|
+
|
|
185
|
+
let field;
|
|
186
|
+
|
|
187
|
+
if (field_index === -1) {
|
|
188
|
+
// not in schema yet, build
|
|
189
|
+
field = new FieldDescriptor();
|
|
190
|
+
field.name = key;
|
|
191
|
+
field.adapter = new FieldValueAdapter();
|
|
192
|
+
|
|
193
|
+
result.push(field);
|
|
194
|
+
} else {
|
|
195
|
+
field = result[field_index];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let actual_type = field.type;
|
|
199
|
+
|
|
200
|
+
if (actual_type === undefined || actual_type === null) {
|
|
201
|
+
let actual_value;
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
actual_value = field.adapter.read(object, key);
|
|
205
|
+
} catch (e) {
|
|
206
|
+
// silent failure
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (actual_value !== undefined && actual_value !== null) {
|
|
210
|
+
actual_type = objectToClass(actual_value);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (property.type) {
|
|
215
|
+
// validate type
|
|
216
|
+
if (actual_type !== undefined && actual_type !== null) {
|
|
217
|
+
if (actual_type !== property.type) {
|
|
218
|
+
console.error(`Schema violation at field '${key}', schema type is different from actual type`, schema, actual_type);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
field.type = property.type;
|
|
223
|
+
} else if (actual_type !== undefined && field.type !== actual_type) {
|
|
224
|
+
field.type = actual_type;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
field.schema = property;
|
|
229
|
+
|
|
230
|
+
if (property.transient === true) {
|
|
231
|
+
// no point in editing transient
|
|
232
|
+
result.splice(field_index, 1);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return result;
|
|
134
239
|
}
|
|
135
240
|
|
|
241
|
+
const build_path = [];
|
|
242
|
+
let build_path_offset = 0;
|
|
243
|
+
|
|
244
|
+
|
|
136
245
|
/**
|
|
137
|
-
*
|
|
138
246
|
* @param {Object} object
|
|
139
|
-
* @param {Map<*,
|
|
247
|
+
* @param {Map<*, TypeEditor>} registry
|
|
248
|
+
* @param {FieldDescriptor} [field_descriptor]
|
|
249
|
+
*/
|
|
250
|
+
export function buildObjectEditorFromRegistry(object, registry, field_descriptor) {
|
|
251
|
+
|
|
252
|
+
const editor = findNearestRegisteredType(registry, object);
|
|
253
|
+
|
|
254
|
+
let fd;
|
|
255
|
+
|
|
256
|
+
if (field_descriptor === undefined) {
|
|
257
|
+
|
|
258
|
+
fd = new FieldDescriptor();
|
|
259
|
+
fd.adapter = new FieldValueAdapter();
|
|
260
|
+
|
|
261
|
+
fd.adapter.read = () => object;
|
|
262
|
+
fd.adapter.writable = false;
|
|
263
|
+
|
|
264
|
+
if (editor.schema) {
|
|
265
|
+
fd.schema = editor.schema;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
} else {
|
|
269
|
+
fd = field_descriptor;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return editor.build(null, fd, registry);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const MAX_DEPTH = 7;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* @param {Object} object
|
|
279
|
+
* @param {Map<*, TypeEditor>} registry
|
|
140
280
|
*/
|
|
141
281
|
export function createObjectEditor(object, registry) {
|
|
142
|
-
const fields = extractObjectFields(object);
|
|
143
282
|
|
|
144
|
-
|
|
283
|
+
if (build_path_offset > MAX_DEPTH) {
|
|
284
|
+
// too deep
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const seen_index = build_path.indexOf(object);
|
|
289
|
+
if (seen_index >= 0 && seen_index < build_path_offset) {
|
|
290
|
+
// recursion
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
build_path[build_path_offset++] = object;
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
const vResult = new EmptyView({
|
|
299
|
+
classList: ['auto-object-editor'],
|
|
300
|
+
css: {}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
const fields = extractObjectFields(object, registry);
|
|
305
|
+
|
|
306
|
+
const field_views = [];
|
|
145
307
|
|
|
146
308
|
for (let i = 0; i < fields.length; i++) {
|
|
147
309
|
const field = fields[i];
|
|
148
310
|
|
|
149
|
-
|
|
311
|
+
let vField;
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
vField = createFieldEditor(object, field, registry);
|
|
315
|
+
} catch (e) {
|
|
316
|
+
vField = new LabelView('ERROR');
|
|
317
|
+
console.warn(e);
|
|
318
|
+
}
|
|
150
319
|
|
|
151
|
-
|
|
320
|
+
if (vField === null) {
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
field_views.push(vField);
|
|
152
325
|
}
|
|
153
326
|
|
|
327
|
+
build_path_offset--;
|
|
328
|
+
|
|
329
|
+
if (field_views.length === 0) {
|
|
330
|
+
// skip containers with no data
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
vResult.addChildren(field_views);
|
|
335
|
+
|
|
154
336
|
return vResult;
|
|
155
337
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { TypeEditor } from "../TypeEditor.js";
|
|
2
|
+
import EmptyView from "../../../../view/elements/EmptyView.js";
|
|
3
|
+
import { rgb2hex } from "../../../../core/color/rgb2hex.js";
|
|
4
|
+
|
|
5
|
+
export class ColorEditor extends TypeEditor {
|
|
6
|
+
build(parent, field) {
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @type {Color}
|
|
10
|
+
*/
|
|
11
|
+
const color = field.adapter.read(parent, field.name);
|
|
12
|
+
|
|
13
|
+
const view = new EmptyView({
|
|
14
|
+
tag: 'input'
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
view.attr({
|
|
18
|
+
type: "color"
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
function sync_up() {
|
|
22
|
+
view.attr({
|
|
23
|
+
value: `#${rgb2hex(color.r * 255, color.g * 255, color.b * 255)}`
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function sync_down(e) {
|
|
28
|
+
const color_value = e.target.value;
|
|
29
|
+
|
|
30
|
+
color.parse(color_value);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
view.on.linked.add(sync_up);
|
|
34
|
+
view.el.addEventListener('input', sync_down, false);
|
|
35
|
+
view.el.addEventListener('change', sync_down, false);
|
|
36
|
+
|
|
37
|
+
return view;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { TypeEditor } from "../TypeEditor.js";
|
|
2
|
+
import View from "../../../../view/View.js";
|
|
3
|
+
|
|
4
|
+
export class HTMLElementEditor extends TypeEditor {
|
|
5
|
+
build(parent, field, registry) {
|
|
6
|
+
const el = field.adapter?.read(parent, field.name);
|
|
7
|
+
|
|
8
|
+
const view = new View();
|
|
9
|
+
|
|
10
|
+
view.el = el;
|
|
11
|
+
|
|
12
|
+
// make the size small
|
|
13
|
+
view.size.setScalar(32);
|
|
14
|
+
|
|
15
|
+
return view;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { TypeEditor } from "../TypeEditor.js";
|
|
2
|
+
import { StringEditor } from "./primitive/StringEditor.js";
|
|
3
|
+
import EmptyView from "../../../../view/elements/EmptyView.js";
|
|
4
|
+
import ImageView from "../../../../view/elements/image/ImageView.js";
|
|
5
|
+
import ObservedString from "../../../../core/model/ObservedString.js";
|
|
6
|
+
import { ObservedStringEditor } from "./ObservedStringEditor.js";
|
|
7
|
+
|
|
8
|
+
export class ImagePathEditor extends TypeEditor {
|
|
9
|
+
inline = true;
|
|
10
|
+
|
|
11
|
+
build(parent, field, registry) {
|
|
12
|
+
let url_editor;
|
|
13
|
+
|
|
14
|
+
if (field.type === String) {
|
|
15
|
+
|
|
16
|
+
url_editor = new StringEditor();
|
|
17
|
+
|
|
18
|
+
} else if (field.type === ObservedString) {
|
|
19
|
+
|
|
20
|
+
url_editor = new ObservedStringEditor();
|
|
21
|
+
|
|
22
|
+
} else {
|
|
23
|
+
throw new Error(`Unsupported type`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const vEditor = url_editor.build(parent, field, registry);
|
|
27
|
+
|
|
28
|
+
const r = new EmptyView({
|
|
29
|
+
css: {
|
|
30
|
+
display: "flex",
|
|
31
|
+
flexDirection: "row"
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const vPreview = new ImageView(field.adapter.read(parent, field.name));
|
|
36
|
+
|
|
37
|
+
vPreview.size.set(32, 32);
|
|
38
|
+
|
|
39
|
+
function update() {
|
|
40
|
+
vPreview.__setSource(vEditor.el.value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
r.addChild(vPreview);
|
|
44
|
+
r.addChild(vEditor);
|
|
45
|
+
|
|
46
|
+
vEditor.el.addEventListener('input', update);
|
|
47
|
+
|
|
48
|
+
return r;
|
|
49
|
+
}
|
|
50
|
+
}
|