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