@playcanvas/web-components 0.13.1 → 0.15.0
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/colors.d.cts +1 -1
- package/dist/colors.d.ts +1 -1
- package/dist/components/joint-component.d.cts +521 -0
- package/dist/components/joint-component.d.ts +521 -0
- package/dist/custom-elements.json +1029 -0
- package/dist/entity-base.d.cts +1 -7
- package/dist/entity-base.d.ts +1 -7
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/loading-bar.d.cts +1 -35
- package/dist/loading-bar.d.ts +1 -35
- package/dist/material.d.cts +13 -0
- package/dist/material.d.ts +13 -0
- package/dist/model.d.cts +71 -0
- package/dist/model.d.ts +71 -0
- package/dist/node.d.cts +55 -0
- package/dist/node.d.ts +55 -0
- package/dist/parse.d.cts +1 -130
- package/dist/parse.d.ts +1 -130
- package/dist/pwc.cjs +1410 -148
- package/dist/pwc.cjs.map +1 -1
- package/dist/pwc.js +1410 -148
- package/dist/pwc.js.map +1 -1
- package/dist/pwc.min.js +1 -1
- package/dist/pwc.min.js.map +1 -1
- package/dist/pwc.min.mjs +1 -1
- package/dist/pwc.min.mjs.map +1 -1
- package/dist/pwc.mjs +1410 -149
- package/dist/pwc.mjs.map +1 -1
- package/dist/vscode.html-custom-data.json +203 -1
- package/dist/web-types.json +368 -2
- package/package.json +5 -4
- package/src/app.ts +18 -0
- package/src/colors.ts +5 -0
- package/src/components/joint-component.ts +984 -0
- package/src/entity-base.ts +3 -3
- package/src/entity.ts +28 -7
- package/src/index.ts +6 -0
- package/src/loading-bar.ts +2 -3
- package/src/material.ts +29 -0
- package/src/model.ts +156 -0
- package/src/node.ts +279 -9
- package/src/parse.ts +11 -1
package/src/node.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { Entity, EventHandle, GraphNode, Quat } from 'playcanvas';
|
|
1
|
+
import type { Entity, EventHandle, GraphNode, Material, MeshInstance, Quat, RenderComponent } from 'playcanvas';
|
|
2
2
|
import { Vec3 } from 'playcanvas';
|
|
3
3
|
|
|
4
4
|
import { ComponentElement } from './components/component';
|
|
5
|
+
import { buildDescendantEntities } from './entity';
|
|
5
6
|
import type { EntityElement } from './entity';
|
|
6
7
|
import { EntityBaseElement, POINTER_ATTRIBUTES } from './entity-base';
|
|
8
|
+
import { MaterialElement } from './material';
|
|
7
9
|
import { ModelElement } from './model';
|
|
8
10
|
import { parseBool, parseTags, parseVec3 } from './parse';
|
|
9
11
|
|
|
@@ -27,6 +29,95 @@ type AuthoredState = {
|
|
|
27
29
|
tags?: string[];
|
|
28
30
|
};
|
|
29
31
|
|
|
32
|
+
/**
|
|
33
|
+
* A sparse mapping from selector to `pc-material` id, as carried by the `material-overrides`
|
|
34
|
+
* attribute and `materialOverrides` property. A `name:X` key selects every mesh instance of the
|
|
35
|
+
* bound node's render component whose baseline material is named `X`; an `index:N` key selects
|
|
36
|
+
* mesh instance `N` and wins over a name rule for the same instance.
|
|
37
|
+
*/
|
|
38
|
+
type MaterialOverrides = Readonly<Record<string, string>>;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* One baseline assignment, captured for every mesh instance of the authored render component
|
|
42
|
+
* when the first material override applies: the mesh instance, the material it displaced, and
|
|
43
|
+
* that material's name at capture time — the name `name:` selectors match, immune to later
|
|
44
|
+
* renames. `material` is `null` when a script had already cleared the assignment.
|
|
45
|
+
*/
|
|
46
|
+
type BaselineAssignment = {
|
|
47
|
+
meshInstance: MeshInstance;
|
|
48
|
+
material: Material | null;
|
|
49
|
+
name: string | null;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** One selector of a material-overrides mapping, in parsed form. */
|
|
53
|
+
type MaterialRule = { kind: 'name'; name: string; id: string } | { kind: 'index'; index: number; id: string };
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Parses one mapping into its valid rules, warning for each entry that is not one: an unknown
|
|
57
|
+
* or missing selector prefix, an empty `name:` value, an `index:` value that is not a
|
|
58
|
+
* non-negative integer, or a replacement id that is not a non-empty string. An invalid rule
|
|
59
|
+
* behaves exactly as if absent from the mapping.
|
|
60
|
+
*
|
|
61
|
+
* @param overrides - The mapping to parse.
|
|
62
|
+
* @param label - The element description for warnings.
|
|
63
|
+
* @returns The valid rules.
|
|
64
|
+
*/
|
|
65
|
+
const parseMaterialRules = (overrides: MaterialOverrides, label: string): MaterialRule[] => {
|
|
66
|
+
const rules: MaterialRule[] = [];
|
|
67
|
+
for (const [selector, id] of Object.entries(overrides)) {
|
|
68
|
+
if (typeof id !== 'string' || id === '') {
|
|
69
|
+
console.warn(`${label} material-overrides '${selector}' needs a pc-material id - rule ignored`);
|
|
70
|
+
} else if (selector.startsWith('name:')) {
|
|
71
|
+
// The text after the prefix is the selector value, exactly as written - a material
|
|
72
|
+
// name may legitimately begin or end with whitespace
|
|
73
|
+
const name = selector.slice('name:'.length);
|
|
74
|
+
if (name === '') {
|
|
75
|
+
console.warn(`${label} material-overrides 'name:' selector is empty - rule ignored`);
|
|
76
|
+
} else {
|
|
77
|
+
rules.push({ kind: 'name', name, id });
|
|
78
|
+
}
|
|
79
|
+
} else if (selector.startsWith('index:')) {
|
|
80
|
+
// Whitespace around the number is tolerated; Number('') is 0, so blank means NaN
|
|
81
|
+
const text = selector.slice('index:'.length).trim();
|
|
82
|
+
const index = text === '' ? NaN : Number(text);
|
|
83
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
84
|
+
console.warn(
|
|
85
|
+
`${label} material-overrides '${selector}' is not a non-negative integer index - rule ignored`
|
|
86
|
+
);
|
|
87
|
+
} else {
|
|
88
|
+
rules.push({ kind: 'index', index, id });
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
console.warn(`${label} material-overrides '${selector}' has no 'name:' or 'index:' prefix - rule ignored`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return rules;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Parses the material-overrides attribute text. Anything but a JSON object — malformed JSON, an
|
|
99
|
+
* array, a primitive — warns and yields `null`, the absent mapping: a stale mapping must not
|
|
100
|
+
* survive an attribute value the DOM no longer represents.
|
|
101
|
+
*
|
|
102
|
+
* @param text - The attribute text.
|
|
103
|
+
* @param label - The element description for warnings.
|
|
104
|
+
* @returns The mapping, or `null`.
|
|
105
|
+
*/
|
|
106
|
+
const parseMaterialOverridesAttribute = (text: string, label: string): MaterialOverrides | null => {
|
|
107
|
+
let parsed: unknown;
|
|
108
|
+
try {
|
|
109
|
+
parsed = JSON.parse(text);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.warn(`${label} material-overrides is not valid JSON - treated as absent: ${(error as Error).message}`);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
115
|
+
console.warn(`${label} material-overrides must be a JSON object - treated as absent`);
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
return parsed as MaterialOverrides;
|
|
119
|
+
};
|
|
120
|
+
|
|
30
121
|
/**
|
|
31
122
|
* Computes the Levenshtein distance between two strings, for near-miss suggestions in the
|
|
32
123
|
* resolution warnings.
|
|
@@ -83,6 +174,13 @@ const levenshtein = (a: string, b: string): number => {
|
|
|
83
174
|
* "x y z" triple.
|
|
84
175
|
* @attribute {string} scale - Overrides the node's local scale, as an "x y z" triple.
|
|
85
176
|
* @attribute {string} tags - Overrides the node's tags, separated by spaces or commas.
|
|
177
|
+
* @attribute {string} material-overrides - Overrides material assignments on the bound node's
|
|
178
|
+
* render component, as a JSON object from selector to `pc-material` id — for example
|
|
179
|
+
* `{"name:CarPaint": "candy-red", "index:7": "smoked-glass"}`. A `name:X` key selects every mesh
|
|
180
|
+
* instance whose baseline material is named `X`; an `index:N` key selects mesh instance `N` and
|
|
181
|
+
* wins over a name rule for the same instance. Assignments no rule matches keep their baseline
|
|
182
|
+
* materials, and removing the attribute restores all of them. Use `pc-model.hierarchy()` to
|
|
183
|
+
* discover the names and indices a node offers.
|
|
86
184
|
* @attribute {string} onpointerenter - Script to run when the pointer moves onto the node.
|
|
87
185
|
* @attribute {string} onpointerleave - Script to run when the pointer moves off the node.
|
|
88
186
|
* @attribute {string} onpointermove - Script to run when the pointer moves over the node.
|
|
@@ -126,6 +224,20 @@ class NodeElement extends EntityBaseElement {
|
|
|
126
224
|
/** The authored values displaced by this element's overrides, captured per property. */
|
|
127
225
|
private _authored: AuthoredState = {};
|
|
128
226
|
|
|
227
|
+
/**
|
|
228
|
+
* The model-authored render component of the bound node, recorded at bind — before child
|
|
229
|
+
* decorations build — so a render component added later by a child `pc-render` can never
|
|
230
|
+
* become the override target. `null` when the bound node has none.
|
|
231
|
+
*/
|
|
232
|
+
private _authoredRender: RenderComponent | null = null;
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* The baseline assignments displaced by the material overrides, captured for every mesh
|
|
236
|
+
* instance when the first non-empty mapping applies and released when the mapping goes
|
|
237
|
+
* absent (restoring them) or the binding dissolves.
|
|
238
|
+
*/
|
|
239
|
+
private _baseline: BaselineAssignment[] | null = null;
|
|
240
|
+
|
|
129
241
|
// Override values. `null` means "no override": the authored value stays in force.
|
|
130
242
|
|
|
131
243
|
private _enabled: boolean | null = null;
|
|
@@ -138,6 +250,8 @@ class NodeElement extends EntityBaseElement {
|
|
|
138
250
|
|
|
139
251
|
private _tags: string[] | null = null;
|
|
140
252
|
|
|
253
|
+
private _materialOverrides: MaterialOverrides | null = null;
|
|
254
|
+
|
|
141
255
|
/**
|
|
142
256
|
* The binding state: `pending` until the host instantiates and `name` resolves, `bound`
|
|
143
257
|
* once decorated, `missing`/`ambiguous`/`duplicate` when resolution failed (each also
|
|
@@ -298,6 +412,7 @@ class NodeElement extends EntityBaseElement {
|
|
|
298
412
|
this._destroyHandle = target.once('destroy', this._onEntityDestroy, this);
|
|
299
413
|
this._state = 'bound';
|
|
300
414
|
this._path = this._pathOf(target, hostEntity);
|
|
415
|
+
this._authoredRender = target.render ?? null;
|
|
301
416
|
|
|
302
417
|
this._applyOverrides();
|
|
303
418
|
this._onReady();
|
|
@@ -333,6 +448,7 @@ class NodeElement extends EntityBaseElement {
|
|
|
333
448
|
this._entity = null;
|
|
334
449
|
this._path = null;
|
|
335
450
|
this._authored = {};
|
|
451
|
+
this._authoredRender = null;
|
|
336
452
|
|
|
337
453
|
// Component decorations come off through the same hook the host-ready cycle uses. A
|
|
338
454
|
// dissolve that never rebinds fires no ready event, so the sweep is explicit - after
|
|
@@ -357,6 +473,9 @@ class NodeElement extends EntityBaseElement {
|
|
|
357
473
|
this._entity = null;
|
|
358
474
|
this._path = null;
|
|
359
475
|
this._authored = {};
|
|
476
|
+
this._authoredRender = null;
|
|
477
|
+
// The mesh instances died with the entity - the capture is dropped, not restored
|
|
478
|
+
this._baseline = null;
|
|
360
479
|
this._state = 'pending';
|
|
361
480
|
this._resetReady();
|
|
362
481
|
}
|
|
@@ -372,13 +491,7 @@ class NodeElement extends EntityBaseElement {
|
|
|
372
491
|
if (!app) {
|
|
373
492
|
return;
|
|
374
493
|
}
|
|
375
|
-
|
|
376
|
-
childEntities.forEach((child) => {
|
|
377
|
-
child._createEntity(app);
|
|
378
|
-
});
|
|
379
|
-
childEntities.forEach((child) => {
|
|
380
|
-
child._buildHierarchy(app);
|
|
381
|
-
});
|
|
494
|
+
buildDescendantEntities(this, app);
|
|
382
495
|
}
|
|
383
496
|
|
|
384
497
|
/**
|
|
@@ -400,6 +513,9 @@ class NodeElement extends EntityBaseElement {
|
|
|
400
513
|
if (this._tags !== null) {
|
|
401
514
|
this.tags = this._tags;
|
|
402
515
|
}
|
|
516
|
+
if (this._materialOverrides !== null) {
|
|
517
|
+
this._applyMaterialOverrides();
|
|
518
|
+
}
|
|
403
519
|
}
|
|
404
520
|
|
|
405
521
|
/**
|
|
@@ -426,6 +542,120 @@ class NodeElement extends EntityBaseElement {
|
|
|
426
542
|
entity.tags.add(authored.tags);
|
|
427
543
|
}
|
|
428
544
|
this._authored = {};
|
|
545
|
+
this._restoreBaseline();
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Applies the material mapping to the authored render component: parse the mapping's valid
|
|
550
|
+
* rules, capture the baseline on first application, then recompute every assignment from
|
|
551
|
+
* that baseline - name rules write over it, index rules write over them, so `index:` wins -
|
|
552
|
+
* and assign whatever changed. An absent mapping, or one with no valid rules, restores the
|
|
553
|
+
* baseline instead. Called while bound, from `_applyOverrides` and the property setter.
|
|
554
|
+
*/
|
|
555
|
+
private _applyMaterialOverrides() {
|
|
556
|
+
const label = `pc-node '${this._name}'`;
|
|
557
|
+
|
|
558
|
+
const rules = this._materialOverrides ? parseMaterialRules(this._materialOverrides, label) : [];
|
|
559
|
+
if (rules.length === 0) {
|
|
560
|
+
this._restoreBaseline();
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if (!this._baseline) {
|
|
565
|
+
if (!this._authoredRender) {
|
|
566
|
+
console.warn(
|
|
567
|
+
`${label} is bound to a node without an authored render component - material-overrides ignored`
|
|
568
|
+
);
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
this._baseline = this._authoredRender.meshInstances.map((meshInstance) => ({
|
|
572
|
+
meshInstance,
|
|
573
|
+
material: (meshInstance.material as Material | null) ?? null,
|
|
574
|
+
name: meshInstance.material?.name ?? null
|
|
575
|
+
}));
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const baseline = this._baseline;
|
|
579
|
+
|
|
580
|
+
/** Resolves a replacement id, warning when it does not resolve. */
|
|
581
|
+
const resolveReplacement = (id: string): Material | null => {
|
|
582
|
+
const material = MaterialElement.get(id);
|
|
583
|
+
if (!material) {
|
|
584
|
+
console.warn(`${label} material-overrides could not resolve pc-material '${id}' - rule ignored`);
|
|
585
|
+
}
|
|
586
|
+
return material ?? null;
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
// Recompute the whole list from the baseline: name rules write over it, index rules
|
|
590
|
+
// write over them. Recomputing makes mapping edits order-independent, and a rule whose
|
|
591
|
+
// replacement does not resolve simply leaves the layer below it in force.
|
|
592
|
+
const resolved = baseline.map((assignment) => assignment.material);
|
|
593
|
+
|
|
594
|
+
for (const rule of rules) {
|
|
595
|
+
if (rule.kind !== 'name') {
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
const material = resolveReplacement(rule.id);
|
|
599
|
+
if (!material) {
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
let matched = false;
|
|
603
|
+
baseline.forEach((assignment, index) => {
|
|
604
|
+
if (assignment.name === rule.name) {
|
|
605
|
+
resolved[index] = material;
|
|
606
|
+
matched = true;
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
if (!matched) {
|
|
610
|
+
const names = baseline.map((assignment) => `'${assignment.name}'`).join(', ');
|
|
611
|
+
console.warn(
|
|
612
|
+
`${label} material-overrides 'name:${rule.name}' matches no assignment - ` +
|
|
613
|
+
`baseline names: ${names || '(none)'}`
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
for (const rule of rules) {
|
|
619
|
+
if (rule.kind !== 'index') {
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
if (rule.index >= baseline.length) {
|
|
623
|
+
console.warn(
|
|
624
|
+
`${label} material-overrides 'index:${rule.index}' is out of range - ` +
|
|
625
|
+
`${baseline.length} assignment(s)`
|
|
626
|
+
);
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
const material = resolveReplacement(rule.id);
|
|
630
|
+
if (material) {
|
|
631
|
+
resolved[rule.index] = material;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
baseline.forEach((assignment, index) => {
|
|
636
|
+
// The engine setter rebuilds material and shader state even for a redundant write,
|
|
637
|
+
// so only actual changes are assigned
|
|
638
|
+
if (assignment.meshInstance.material !== resolved[index]) {
|
|
639
|
+
assignment.meshInstance.material = resolved[index] as Material;
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Restores every baseline assignment the material overrides displaced and releases the
|
|
646
|
+
* capture, so the next non-empty mapping captures afresh. Safe to call without a capture.
|
|
647
|
+
*/
|
|
648
|
+
private _restoreBaseline() {
|
|
649
|
+
const baseline = this._baseline;
|
|
650
|
+
if (!baseline) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
this._baseline = null;
|
|
654
|
+
for (const assignment of baseline) {
|
|
655
|
+
if (assignment.meshInstance.material !== assignment.material) {
|
|
656
|
+
assignment.meshInstance.material = assignment.material as Material;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
429
659
|
}
|
|
430
660
|
|
|
431
661
|
/**
|
|
@@ -659,8 +889,43 @@ class NodeElement extends EntityBaseElement {
|
|
|
659
889
|
return this._tags;
|
|
660
890
|
}
|
|
661
891
|
|
|
892
|
+
/**
|
|
893
|
+
* Sets the material overrides: a sparse mapping from selector to `pc-material` id, applied
|
|
894
|
+
* to the bound node's authored render component. A `name:X` key selects every mesh instance
|
|
895
|
+
* whose baseline material is named `X`; an `index:N` key selects mesh instance `N` and wins
|
|
896
|
+
* over a name rule for the same instance. Assignments no rule matches keep their baseline
|
|
897
|
+
* materials. `null` clears the mapping, restoring every baseline assignment.
|
|
898
|
+
* @param value - The mapping, or `null`.
|
|
899
|
+
*/
|
|
900
|
+
set materialOverrides(value: MaterialOverrides | null) {
|
|
901
|
+
// Copied and frozen: later caller mutation of the passed object must not silently
|
|
902
|
+
// disagree with the mapping the element applied
|
|
903
|
+
this._materialOverrides = value === null ? null : Object.freeze({ ...value });
|
|
904
|
+
if (this._state === 'bound') {
|
|
905
|
+
this._applyMaterialOverrides();
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Gets the material overrides.
|
|
911
|
+
* @returns The mapping, or `null` while no override is set.
|
|
912
|
+
*/
|
|
913
|
+
get materialOverrides(): MaterialOverrides | null {
|
|
914
|
+
return this._materialOverrides;
|
|
915
|
+
}
|
|
916
|
+
|
|
662
917
|
static get observedAttributes() {
|
|
663
|
-
return [
|
|
918
|
+
return [
|
|
919
|
+
'enabled',
|
|
920
|
+
'index',
|
|
921
|
+
'material-overrides',
|
|
922
|
+
'name',
|
|
923
|
+
'position',
|
|
924
|
+
'rotation',
|
|
925
|
+
'scale',
|
|
926
|
+
'tags',
|
|
927
|
+
...POINTER_ATTRIBUTES
|
|
928
|
+
];
|
|
664
929
|
}
|
|
665
930
|
|
|
666
931
|
attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
|
|
@@ -684,6 +949,10 @@ class NodeElement extends EntityBaseElement {
|
|
|
684
949
|
}
|
|
685
950
|
}
|
|
686
951
|
break;
|
|
952
|
+
case 'material-overrides':
|
|
953
|
+
this.materialOverrides =
|
|
954
|
+
newValue === null ? null : parseMaterialOverridesAttribute(newValue, `pc-node '${this._name}'`);
|
|
955
|
+
break;
|
|
687
956
|
case 'name':
|
|
688
957
|
this.name = newValue ?? '';
|
|
689
958
|
break;
|
|
@@ -713,3 +982,4 @@ class NodeElement extends EntityBaseElement {
|
|
|
713
982
|
customElements.define('pc-node', NodeElement);
|
|
714
983
|
|
|
715
984
|
export { NodeElement };
|
|
985
|
+
export type { MaterialOverrides };
|
package/src/parse.ts
CHANGED
|
@@ -28,7 +28,7 @@ import { CSS_COLORS } from './colors';
|
|
|
28
28
|
* @param value - The value to split.
|
|
29
29
|
* @param count - The required number of components.
|
|
30
30
|
* @returns The parsed components, or `null`.
|
|
31
|
-
* @
|
|
31
|
+
* @internal
|
|
32
32
|
*/
|
|
33
33
|
export const parseComponents = (value: string, count: number): number[] | null => {
|
|
34
34
|
const components = value.trim().split(/\s+/).map(Number);
|
|
@@ -61,6 +61,7 @@ const cloneDefault = <T extends Color | Quat | Vec2 | Vec3 | Vec4 | null>(value:
|
|
|
61
61
|
* @param value - The attribute value to parse (`null` when the attribute is absent).
|
|
62
62
|
* @param defaultValue - The value to use when the attribute is absent or removed.
|
|
63
63
|
* @returns The parsed boolean.
|
|
64
|
+
* @internal
|
|
64
65
|
*/
|
|
65
66
|
export const parseBool = (value: string | null, defaultValue: boolean): boolean => {
|
|
66
67
|
return value === null ? defaultValue : value !== 'false';
|
|
@@ -77,6 +78,7 @@ export const parseBool = (value: string | null, defaultValue: boolean): boolean
|
|
|
77
78
|
* @param defaultValue - The value to use when the attribute is absent or invalid.
|
|
78
79
|
* @param attribute - The attribute name, used in the warning message.
|
|
79
80
|
* @returns The parsed Color object.
|
|
81
|
+
* @internal
|
|
80
82
|
*/
|
|
81
83
|
export const parseColor = <T extends Color | null>(
|
|
82
84
|
value: string | null,
|
|
@@ -129,6 +131,7 @@ export const parseColor = <T extends Color | null>(
|
|
|
129
131
|
* @param defaultValue - The value to use when the attribute is absent or invalid.
|
|
130
132
|
* @param attribute - The attribute name, used in the warning message.
|
|
131
133
|
* @returns The resolved enum name.
|
|
134
|
+
* @internal
|
|
132
135
|
*/
|
|
133
136
|
export const parseEnum = <T extends string>(
|
|
134
137
|
value: string | null,
|
|
@@ -158,6 +161,7 @@ export const parseEnum = <T extends string>(
|
|
|
158
161
|
* @param defaultValue - The value to use when the attribute is absent or invalid.
|
|
159
162
|
* @param attribute - The attribute name, used in the warning message.
|
|
160
163
|
* @returns The parsed number.
|
|
164
|
+
* @internal
|
|
161
165
|
*/
|
|
162
166
|
export const parseNumber = <T extends number | null>(
|
|
163
167
|
value: string | null,
|
|
@@ -187,6 +191,7 @@ export const parseNumber = <T extends number | null>(
|
|
|
187
191
|
* @param defaultValue - The value to use when the attribute is absent or invalid.
|
|
188
192
|
* @param attribute - The attribute name, used in the warning message.
|
|
189
193
|
* @returns The parsed Quat object.
|
|
194
|
+
* @internal
|
|
190
195
|
*/
|
|
191
196
|
export const parseQuat = <T extends Quat | null>(
|
|
192
197
|
value: string | null,
|
|
@@ -217,6 +222,7 @@ export const parseQuat = <T extends Quat | null>(
|
|
|
217
222
|
* @param value - The attribute value to parse (`null` when the attribute is absent).
|
|
218
223
|
* @param defaultValue - The value to use when the attribute is absent or removed.
|
|
219
224
|
* @returns The parsed tag names.
|
|
225
|
+
* @internal
|
|
220
226
|
*/
|
|
221
227
|
export const parseTags = (value: string | null, defaultValue: string[] = []): string[] => {
|
|
222
228
|
if (value === null) {
|
|
@@ -239,6 +245,7 @@ export const parseTags = (value: string | null, defaultValue: string[] = []): st
|
|
|
239
245
|
* @param defaultValue - The value to use when the attribute is absent or invalid.
|
|
240
246
|
* @param attribute - The attribute name, used in the warning message.
|
|
241
247
|
* @returns The parsed Vec2 object.
|
|
248
|
+
* @internal
|
|
242
249
|
*/
|
|
243
250
|
export const parseVec2 = <T extends Vec2 | null>(
|
|
244
251
|
value: string | null,
|
|
@@ -267,6 +274,7 @@ export const parseVec2 = <T extends Vec2 | null>(
|
|
|
267
274
|
* @param defaultValue - The value to use when the attribute is absent or invalid.
|
|
268
275
|
* @param attribute - The attribute name, used in the warning message.
|
|
269
276
|
* @returns The parsed Vec3 object.
|
|
277
|
+
* @internal
|
|
270
278
|
*/
|
|
271
279
|
export const parseVec3 = <T extends Vec3 | null>(
|
|
272
280
|
value: string | null,
|
|
@@ -295,6 +303,7 @@ export const parseVec3 = <T extends Vec3 | null>(
|
|
|
295
303
|
* @param defaultValue - The value to use when the attribute is absent or invalid.
|
|
296
304
|
* @param attribute - The attribute name, used in the warning message.
|
|
297
305
|
* @returns The parsed Vec4 object.
|
|
306
|
+
* @internal
|
|
298
307
|
*/
|
|
299
308
|
export const parseVec4 = <T extends Vec4 | null>(
|
|
300
309
|
value: string | null,
|
|
@@ -321,6 +330,7 @@ export const parseVec4 = <T extends Vec4 | null>(
|
|
|
321
330
|
*
|
|
322
331
|
* @param ref - The reference string to resolve.
|
|
323
332
|
* @returns The resolved entity, or `null`.
|
|
333
|
+
* @internal
|
|
324
334
|
*/
|
|
325
335
|
export const getEntity = (ref: string): Entity | null => {
|
|
326
336
|
if (!ref) {
|