@configura/web-api 1.3.0-alpha.1 → 1.3.0-alpha.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/dist/CatalogueAPI.d.ts +56 -2
- package/dist/CatalogueAPI.js +1 -1
- package/dist/CfgMeasure.d.ts +33 -0
- package/dist/CfgMeasure.js +30 -0
- package/dist/CfgProduct.d.ts +106 -5
- package/dist/CfgProduct.js +117 -71
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/material/CfgMaterialMapping.js +11 -6
- package/dist/material/CfgMtrlApplication.js +4 -4
- package/dist/productConfiguration/CfgFeature.d.ts +30 -4
- package/dist/productConfiguration/CfgFeature.js +162 -52
- package/dist/productConfiguration/CfgOption.d.ts +21 -5
- package/dist/productConfiguration/CfgOption.js +125 -24
- package/dist/productConfiguration/CfgProductConfiguration.d.ts +74 -4
- package/dist/productConfiguration/CfgProductConfiguration.js +158 -49
- package/dist/productConfiguration/utilitiesProductConfiguration.d.ts +9 -1
- package/dist/productConfiguration/utilitiesProductConfiguration.js +32 -13
- package/dist/productLoader.d.ts +22 -0
- package/dist/productLoader.js +22 -14
- package/dist/tests/testData/dummyProductForTest.js +1 -0
- package/dist/tests/testData/testDataAdditionalProductInAdditionalProductInProductForTest.js +13 -0
- package/dist/tests/testData/testDataCachedGetProduct.js +6 -0
- package/dist/tests/testData/testDataCachedPostValidate.js +6 -0
- package/dist/tests/testData/testDataNoAdditionalProductNoPropagateForTest.js +2 -0
- package/dist/tests/testData/testDataProductAggregatedPrice.js +6 -1
- package/dist/tests/testData/testDataUpcharge.js +4 -0
- package/dist/utilitiesCatalogueData.d.ts +5 -1
- package/dist/utilitiesCatalogueData.js +9 -1
- package/dist/utilitiesNumericValues.d.ts +25 -0
- package/dist/utilitiesNumericValues.js +109 -0
- package/package.json +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Observable, SingleArgCallback } from "@configura/web-utilities";
|
|
1
|
+
import { LengthUnit, Observable, SingleArgCallback } from "@configura/web-utilities";
|
|
2
2
|
import { Feature, FeatureRef, SelectedOption } from "../CatalogueAPI.js";
|
|
3
3
|
import { CfgProduct, _CfgProductInternal } from "../CfgProduct.js";
|
|
4
4
|
import { CfgFeature, _CfgFeatureInternal } from "./CfgFeature.js";
|
|
@@ -6,44 +6,114 @@ import { ProductConfigurationBubbleMode } from "./CfgOption.js";
|
|
|
6
6
|
export declare type ProductConfigurationChangeNotification = {
|
|
7
7
|
freshRef: CfgProductConfiguration;
|
|
8
8
|
};
|
|
9
|
+
export declare type LengthValue = {
|
|
10
|
+
length: number;
|
|
11
|
+
unit: LengthUnit;
|
|
12
|
+
};
|
|
13
|
+
export declare type StretchMap = Map<string, {
|
|
14
|
+
def: LengthValue | undefined;
|
|
15
|
+
current: LengthValue;
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* This class is meant to only be used through CfgProductConfiguration. It should never be
|
|
19
|
+
* instantiated on its own. Normally the internal state of this class should never be directly
|
|
20
|
+
* modified. CfgProductConfiguration is the class that should be used and interacted with.
|
|
21
|
+
*/
|
|
9
22
|
export declare class _CfgProductConfigurationInternal {
|
|
10
23
|
readonly allRawFeatures: Feature[];
|
|
11
24
|
readonly parentProduct: _CfgProductInternal;
|
|
12
25
|
readonly rootProduct: _CfgProductInternal;
|
|
13
|
-
static _makeUninitialized
|
|
26
|
+
static _makeUninitialized(rootFeatureRefs: FeatureRef[], allRawFeatures: Feature[], // Flat packed. All the features that can appear anyplace in the selection tree.
|
|
27
|
+
parentProduct: _CfgProductInternal, rootProduct: _CfgProductInternal): _CfgProductConfigurationInternal;
|
|
14
28
|
private constructor();
|
|
15
29
|
readonly key = "~";
|
|
16
30
|
private _rootFeatureRefs;
|
|
17
31
|
private _features;
|
|
32
|
+
readonly stretchReferenceLengthsByMeasureParamCode: StretchMap;
|
|
18
33
|
readonly changeObservable: Observable<ProductConfigurationChangeNotification>;
|
|
19
34
|
get rootFeatureRefs(): FeatureRef[];
|
|
20
35
|
get features(): CfgFeature[];
|
|
21
36
|
private _notifyAllOfChange;
|
|
22
37
|
_freshRefDescendants(): void;
|
|
38
|
+
/** Called by child to tell its parent that it has changed. */
|
|
23
39
|
_childHasChanged: (freshRef: CfgFeature, bubbleMode: ProductConfigurationBubbleMode) => Promise<void>;
|
|
24
40
|
getApiSelection: () => SelectedOption[];
|
|
41
|
+
/**
|
|
42
|
+
* When used internally the notifications are taken care off by the caller, but if set from
|
|
43
|
+
* outside we want notifications to bubble all the way to the root.
|
|
44
|
+
*/
|
|
25
45
|
setApiSelection: (selectedOptions: SelectedOption[], bubbleToRoot: boolean) => Promise<boolean>;
|
|
26
46
|
structureCompare: (other: _CfgProductConfigurationInternal, strictOrder?: boolean, descriptionMatch?: boolean) => boolean;
|
|
47
|
+
/**
|
|
48
|
+
* When used internally the notifications are taken care off by the caller, but if set from
|
|
49
|
+
* outside we want notifications to bubble all the way to the root.
|
|
50
|
+
*/
|
|
27
51
|
tryMatchSelection: (other: _CfgProductConfigurationInternal, descriptionMatch: boolean | undefined, validate: boolean) => Promise<boolean>;
|
|
52
|
+
/** Only selected features. */
|
|
28
53
|
_getFeaturesWithCode: (code: string) => _CfgFeatureInternal[];
|
|
29
54
|
populateFeatures: (rootFeatureRefs: FeatureRef[]) => void;
|
|
55
|
+
setStretchReferenceLength: (measureParamCode: string, referenceLength: number | undefined, unit: LengthUnit, doNotify?: boolean) => Promise<boolean>;
|
|
30
56
|
}
|
|
31
57
|
export declare class CfgProductConfiguration {
|
|
32
58
|
readonly _internal: _CfgProductConfigurationInternal;
|
|
33
|
-
|
|
34
|
-
|
|
59
|
+
/**
|
|
60
|
+
* This method is semi-async. It will immediately give you a reference to the created
|
|
61
|
+
* CfgProductConfiguration, but is not properly initialized until the initDone-callback
|
|
62
|
+
* has been called.
|
|
63
|
+
*/
|
|
64
|
+
static make(initSuccess: (c: CfgProductConfiguration) => void, initFail: (error: Error) => void, rootFeatureRefs: FeatureRef[], allRawFeatures: Feature[], // Flat packed. All the features that can appear anyplace in the selection tree.
|
|
65
|
+
apiSelection: SelectedOption[], parentProduct: _CfgProductInternal, rootProduct: _CfgProductInternal): CfgProductConfiguration;
|
|
66
|
+
/**
|
|
67
|
+
* Makes an object wrapping the passed object. This is not a clone method, it is a method to
|
|
68
|
+
* make a new outer reference. Like a shallow copy.
|
|
69
|
+
*
|
|
70
|
+
* We use this to help frameworks that are built around using equals to detect change.
|
|
71
|
+
*/
|
|
72
|
+
static _makeNewRefFrom(internal: _CfgProductConfigurationInternal): CfgProductConfiguration;
|
|
73
|
+
/**
|
|
74
|
+
* Private constructor and make-method because make new ref requires the constructor to take an
|
|
75
|
+
* internal and we don't want those who instantiate CfgProductConfiguration to have to be aware
|
|
76
|
+
* of the internal.
|
|
77
|
+
*/
|
|
35
78
|
private constructor();
|
|
36
79
|
isBackedBySame: (other: CfgProductConfiguration) => boolean;
|
|
37
80
|
get parentProduct(): CfgProduct;
|
|
38
81
|
get rootProduct(): CfgProduct;
|
|
39
82
|
get key(): string;
|
|
83
|
+
/**
|
|
84
|
+
* Every (unprocessed) feature which might be used in this product. This is constant for a
|
|
85
|
+
* product load. What features are actually used is controlled by rootFeatureRefs and what
|
|
86
|
+
* options are selected.
|
|
87
|
+
*/
|
|
40
88
|
get allRawFeatures(): Feature[];
|
|
89
|
+
/** What features are used in the root of this. This can change with new validate calls. */
|
|
41
90
|
get rootFeatureRefs(): FeatureRef[];
|
|
91
|
+
/** The root features at the root of the selection tree. */
|
|
42
92
|
get features(): CfgFeature[];
|
|
43
93
|
structureCompare: (other: CfgProductConfiguration, strictOrder?: boolean, descriptionMatch?: boolean) => boolean;
|
|
94
|
+
/**
|
|
95
|
+
* This method will try to match the selection from another product configuration
|
|
96
|
+
* This method does not propagate its selections.
|
|
97
|
+
* This method will cause validation calls.
|
|
98
|
+
*/
|
|
44
99
|
tryMatchSelection: (other: CfgProductConfiguration, descriptionMatch?: boolean) => Promise<boolean>;
|
|
45
100
|
getApiSelection: () => SelectedOption[];
|
|
101
|
+
/**
|
|
102
|
+
* This method does not propagate its selections.
|
|
103
|
+
* This method will not cause validation calls. Data is assumed to already be validated.
|
|
104
|
+
*/
|
|
46
105
|
setApiSelection: (selectedOptions: SelectedOption[]) => Promise<boolean>;
|
|
106
|
+
/**
|
|
107
|
+
* Set how stretched a certain measure should be measureParamCode is the measure to be
|
|
108
|
+
* stretched referenceLength is a value relative to the initial length of the measure. If the
|
|
109
|
+
* stretch drawn up in Model Lab was from the start let's say 2.4 units from start point to end
|
|
110
|
+
* point a reference value of 4.8 would stretch the start point to end point to double length.
|
|
111
|
+
* This however does not stop things from extending outside start point to end point. So the
|
|
112
|
+
* actual perceived length after stretching might have little to do with the referenceLength.
|
|
113
|
+
* Undefined as referenceLength will clear, that is, make the stretched thing revert to its
|
|
114
|
+
* default state.
|
|
115
|
+
*/
|
|
116
|
+
setStretchReferenceLength: (measureParamCode: string, referenceLength: number, unit: LengthUnit) => Promise<boolean>;
|
|
47
117
|
listenForChange: (l: SingleArgCallback<ProductConfigurationChangeNotification>) => void;
|
|
48
118
|
stopListenForChange: (l: SingleArgCallback<ProductConfigurationChangeNotification>) => void;
|
|
49
119
|
stopAllListenForChange: () => void;
|
|
@@ -7,15 +7,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { compareArrays, count, Observable, toError, } from "@configura/web-utilities";
|
|
10
|
+
import { compareArrays, count, isLengthUnit, Observable, toError, toLengthUnit, } from "@configura/web-utilities";
|
|
11
11
|
import { CfgProduct } from "../CfgProduct.js";
|
|
12
12
|
import { CfgFeature } from "./CfgFeature.js";
|
|
13
13
|
import { ProductConfigurationBubbleMode } from "./CfgOption.js";
|
|
14
14
|
import { syncCfgFeatures } from "./utilitiesProductConfiguration.js";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
/**
|
|
16
|
+
* This class is meant to only be used through CfgProductConfiguration. It should never be
|
|
17
|
+
* instantiated on its own. Normally the internal state of this class should never be directly
|
|
18
|
+
* modified. CfgProductConfiguration is the class that should be used and interacted with.
|
|
19
|
+
*/
|
|
19
20
|
export class _CfgProductConfigurationInternal {
|
|
20
21
|
constructor(allRawFeatures, // Flat packed. All the features that can appear anyplace in the selection tree.
|
|
21
22
|
parentProduct, rootProduct) {
|
|
@@ -42,7 +43,7 @@ export class _CfgProductConfigurationInternal {
|
|
|
42
43
|
? ProductConfigurationBubbleMode.Stop
|
|
43
44
|
: bubbleMode);
|
|
44
45
|
});
|
|
45
|
-
|
|
46
|
+
/** Called by child to tell its parent that it has changed. */
|
|
46
47
|
this._childHasChanged = (freshRef, bubbleMode) => __awaiter(this, void 0, void 0, function* () {
|
|
47
48
|
const features = this._features;
|
|
48
49
|
const i = features.findIndex((a) => a.isBackedBySame(freshRef));
|
|
@@ -55,8 +56,10 @@ export class _CfgProductConfigurationInternal {
|
|
|
55
56
|
this.getApiSelection = () => this._features.map((f) => {
|
|
56
57
|
return { code: "!~!", next: f._internal.getApiSelection() };
|
|
57
58
|
});
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
/**
|
|
60
|
+
* When used internally the notifications are taken care off by the caller, but if set from
|
|
61
|
+
* outside we want notifications to bubble all the way to the root.
|
|
62
|
+
*/
|
|
60
63
|
this.setApiSelection = (selectedOptions, bubbleToRoot) => __awaiter(this, void 0, void 0, function* () {
|
|
61
64
|
const featuresLength = this._features.length;
|
|
62
65
|
const selectedOptionsLength = selectedOptions.length;
|
|
@@ -72,8 +75,10 @@ export class _CfgProductConfigurationInternal {
|
|
|
72
75
|
return change;
|
|
73
76
|
});
|
|
74
77
|
this.structureCompare = (other, strictOrder = true, descriptionMatch = false) => compareArrays(this.features, other.features, (l, r) => l._internal.structureCompare(r._internal, strictOrder, descriptionMatch), strictOrder);
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
/**
|
|
79
|
+
* When used internally the notifications are taken care off by the caller, but if set from
|
|
80
|
+
* outside we want notifications to bubble all the way to the root.
|
|
81
|
+
*/
|
|
77
82
|
this.tryMatchSelection = (other, descriptionMatch = false, // Match on case insensitive description, not code
|
|
78
83
|
validate) => __awaiter(this, void 0, void 0, function* () {
|
|
79
84
|
const thisFeatures = this.features;
|
|
@@ -101,15 +106,99 @@ export class _CfgProductConfigurationInternal {
|
|
|
101
106
|
}
|
|
102
107
|
return change;
|
|
103
108
|
});
|
|
104
|
-
|
|
109
|
+
/** Only selected features. */
|
|
105
110
|
this._getFeaturesWithCode = (code) => this._features.reduce((agg, feature) => {
|
|
106
111
|
agg.push(...feature._internal._getFeaturesWithCode(code));
|
|
107
112
|
return agg;
|
|
108
113
|
}, []);
|
|
109
114
|
this.populateFeatures = (rootFeatureRefs) => {
|
|
110
115
|
this._rootFeatureRefs = rootFeatureRefs;
|
|
111
|
-
this._features = syncCfgFeatures(rootFeatureRefs, this._features, this.allRawFeatures, this, this.parentProduct, this.rootProduct);
|
|
116
|
+
this._features = syncCfgFeatures(rootFeatureRefs, this._features, this.allRawFeatures, this, this, this.parentProduct, this.rootProduct);
|
|
112
117
|
};
|
|
118
|
+
this.setStretchReferenceLength = (measureParamCode, referenceLength, unit, doNotify = true) => __awaiter(this, void 0, void 0, function* () {
|
|
119
|
+
if (measureParamCode === "") {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
if (referenceLength === undefined) {
|
|
123
|
+
// We assume that referenceLength === undefined shall be ignored.
|
|
124
|
+
// One can add a measureParam to a Feature in Catalogue Creator
|
|
125
|
+
// without actually having any numeric values on the Options.
|
|
126
|
+
// Then this will happen. A previous version of the code assumed
|
|
127
|
+
// undefined meant reset. However, this does not seem consistent
|
|
128
|
+
// with CET.
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
const stretchReferenceLengthsByMeasureParamCode = this.stretchReferenceLengthsByMeasureParamCode;
|
|
132
|
+
let stretchReferenceLength = stretchReferenceLengthsByMeasureParamCode.get(measureParamCode);
|
|
133
|
+
const referenceLengthWithUnit = {
|
|
134
|
+
length: referenceLength,
|
|
135
|
+
unit,
|
|
136
|
+
};
|
|
137
|
+
if (stretchReferenceLength === undefined) {
|
|
138
|
+
stretchReferenceLengthsByMeasureParamCode.set(measureParamCode, {
|
|
139
|
+
def: undefined,
|
|
140
|
+
current: referenceLengthWithUnit,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
if (stretchReferenceLength.current === referenceLengthWithUnit) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
stretchReferenceLength.current = referenceLengthWithUnit;
|
|
148
|
+
}
|
|
149
|
+
if (doNotify) {
|
|
150
|
+
yield this._notifyAllOfChange(ProductConfigurationBubbleMode.ToRoot);
|
|
151
|
+
}
|
|
152
|
+
return true;
|
|
153
|
+
});
|
|
154
|
+
// Useful debug tool:
|
|
155
|
+
if (false) {
|
|
156
|
+
const dbgName = `conf${parentProduct.refKey || parentProduct.key}`;
|
|
157
|
+
window[dbgName] = this;
|
|
158
|
+
window.conf = this;
|
|
159
|
+
console.log(`Use "window['${dbgName}']" or window.conf to access conf.`);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Although the measurement-datas are also passed in validate-calls they are only used at
|
|
163
|
+
* initial product creation. The data is assumed to always be the same for a product, so
|
|
164
|
+
* after a validate call the product configuration values are what controls how stretched
|
|
165
|
+
* things are.
|
|
166
|
+
*/
|
|
167
|
+
const stretchReferenceLengthsByMeasureParamCode = new Map();
|
|
168
|
+
for (const measurementDefinition of parentProduct.measureDefinitions) {
|
|
169
|
+
const { measureParamCode, numericValue } = measurementDefinition;
|
|
170
|
+
if (numericValue === undefined) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
let unit;
|
|
174
|
+
const rawUnit = numericValue.unit;
|
|
175
|
+
if (rawUnit === undefined) {
|
|
176
|
+
unit = parentProduct.unit;
|
|
177
|
+
}
|
|
178
|
+
else if (rawUnit === "") {
|
|
179
|
+
unit = parentProduct.unit;
|
|
180
|
+
console.warn(`MeasureDefinition unit is empty string for code ${measureParamCode}.`);
|
|
181
|
+
}
|
|
182
|
+
else if (isLengthUnit(rawUnit)) {
|
|
183
|
+
unit = toLengthUnit(rawUnit);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
console.info(`MeasureDefinition unit is non length: "${rawUnit}" for code ${measureParamCode}. Ignored for stretch.`);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const referenceLength = { length: numericValue.value, unit };
|
|
190
|
+
stretchReferenceLengthsByMeasureParamCode.set(measureParamCode, {
|
|
191
|
+
def: referenceLength,
|
|
192
|
+
current: referenceLength,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
this.stretchReferenceLengthsByMeasureParamCode = stretchReferenceLengthsByMeasureParamCode;
|
|
196
|
+
}
|
|
197
|
+
static _makeUninitialized(rootFeatureRefs, allRawFeatures, // Flat packed. All the features that can appear anyplace in the selection tree.
|
|
198
|
+
parentProduct, rootProduct) {
|
|
199
|
+
const configuration = new this(allRawFeatures, parentProduct, rootProduct);
|
|
200
|
+
configuration.populateFeatures(rootFeatureRefs);
|
|
201
|
+
return configuration;
|
|
113
202
|
}
|
|
114
203
|
get rootFeatureRefs() {
|
|
115
204
|
return this._rootFeatureRefs;
|
|
@@ -127,33 +216,69 @@ export class _CfgProductConfigurationInternal {
|
|
|
127
216
|
}
|
|
128
217
|
}
|
|
129
218
|
}
|
|
130
|
-
_CfgProductConfigurationInternal._makeUninitialized = (rootFeatureRefs, allRawFeatures, // Flat packed. All the features that can appear anyplace in the selection tree.
|
|
131
|
-
parentProduct, rootProduct) => {
|
|
132
|
-
const configuration = new _CfgProductConfigurationInternal(allRawFeatures, parentProduct, rootProduct);
|
|
133
|
-
configuration.populateFeatures(rootFeatureRefs);
|
|
134
|
-
return configuration;
|
|
135
|
-
};
|
|
136
219
|
export class CfgProductConfiguration {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Private constructor and make-method because make new ref requires the constructor to take an
|
|
222
|
+
* internal and we don't want those who instantiate CfgProductConfiguration to have to be aware
|
|
223
|
+
* of the internal.
|
|
224
|
+
*/
|
|
140
225
|
constructor(_internal) {
|
|
141
226
|
this._internal = _internal;
|
|
142
227
|
this.isBackedBySame = (other) => this._internal === other._internal;
|
|
143
228
|
this.structureCompare = (other, strictOrder = true, descriptionMatch = false) => this._internal.structureCompare(other._internal, strictOrder, descriptionMatch);
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
229
|
+
/**
|
|
230
|
+
* This method will try to match the selection from another product configuration
|
|
231
|
+
* This method does not propagate its selections.
|
|
232
|
+
* This method will cause validation calls.
|
|
233
|
+
*/
|
|
147
234
|
this.tryMatchSelection = (other, descriptionMatch = false // Match on case insensitive description, not code
|
|
148
235
|
) => __awaiter(this, void 0, void 0, function* () { return yield this._internal.tryMatchSelection(other._internal, descriptionMatch, true); });
|
|
149
236
|
this.getApiSelection = () => this._internal.getApiSelection();
|
|
150
|
-
|
|
151
|
-
|
|
237
|
+
/**
|
|
238
|
+
* This method does not propagate its selections.
|
|
239
|
+
* This method will not cause validation calls. Data is assumed to already be validated.
|
|
240
|
+
*/
|
|
152
241
|
this.setApiSelection = (selectedOptions) => __awaiter(this, void 0, void 0, function* () { return yield this._internal.setApiSelection(selectedOptions, true); });
|
|
242
|
+
/**
|
|
243
|
+
* Set how stretched a certain measure should be measureParamCode is the measure to be
|
|
244
|
+
* stretched referenceLength is a value relative to the initial length of the measure. If the
|
|
245
|
+
* stretch drawn up in Model Lab was from the start let's say 2.4 units from start point to end
|
|
246
|
+
* point a reference value of 4.8 would stretch the start point to end point to double length.
|
|
247
|
+
* This however does not stop things from extending outside start point to end point. So the
|
|
248
|
+
* actual perceived length after stretching might have little to do with the referenceLength.
|
|
249
|
+
* Undefined as referenceLength will clear, that is, make the stretched thing revert to its
|
|
250
|
+
* default state.
|
|
251
|
+
*/
|
|
252
|
+
this.setStretchReferenceLength = (measureParamCode, referenceLength, unit) => this._internal.setStretchReferenceLength(measureParamCode, referenceLength, unit);
|
|
153
253
|
this.listenForChange = (l) => this._internal.changeObservable.listen(l);
|
|
154
254
|
this.stopListenForChange = (l) => this._internal.changeObservable.stopListen(l);
|
|
155
255
|
this.stopAllListenForChange = () => this._internal.changeObservable.stopAllListen();
|
|
156
256
|
}
|
|
257
|
+
/**
|
|
258
|
+
* This method is semi-async. It will immediately give you a reference to the created
|
|
259
|
+
* CfgProductConfiguration, but is not properly initialized until the initDone-callback
|
|
260
|
+
* has been called.
|
|
261
|
+
*/
|
|
262
|
+
static make(initSuccess, initFail, rootFeatureRefs, allRawFeatures, // Flat packed. All the features that can appear anyplace in the selection tree.
|
|
263
|
+
apiSelection, parentProduct, rootProduct) {
|
|
264
|
+
const internal = _CfgProductConfigurationInternal._makeUninitialized(rootFeatureRefs, allRawFeatures, parentProduct, rootProduct);
|
|
265
|
+
const external = new this(internal);
|
|
266
|
+
// Note the async-callback at the end of the following line. The call is async.
|
|
267
|
+
internal
|
|
268
|
+
.setApiSelection(apiSelection, false)
|
|
269
|
+
.then(() => initSuccess(external))
|
|
270
|
+
.catch((e) => initFail(toError(e)));
|
|
271
|
+
return external;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Makes an object wrapping the passed object. This is not a clone method, it is a method to
|
|
275
|
+
* make a new outer reference. Like a shallow copy.
|
|
276
|
+
*
|
|
277
|
+
* We use this to help frameworks that are built around using equals to detect change.
|
|
278
|
+
*/
|
|
279
|
+
static _makeNewRefFrom(internal) {
|
|
280
|
+
return new this(internal);
|
|
281
|
+
}
|
|
157
282
|
get parentProduct() {
|
|
158
283
|
return CfgProduct._makeNewRefFrom(this._internal.parentProduct);
|
|
159
284
|
}
|
|
@@ -163,36 +288,20 @@ export class CfgProductConfiguration {
|
|
|
163
288
|
get key() {
|
|
164
289
|
return this._internal.key;
|
|
165
290
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
291
|
+
/**
|
|
292
|
+
* Every (unprocessed) feature which might be used in this product. This is constant for a
|
|
293
|
+
* product load. What features are actually used is controlled by rootFeatureRefs and what
|
|
294
|
+
* options are selected.
|
|
295
|
+
*/
|
|
169
296
|
get allRawFeatures() {
|
|
170
297
|
return this._internal.allRawFeatures;
|
|
171
298
|
}
|
|
172
|
-
|
|
299
|
+
/** What features are used in the root of this. This can change with new validate calls. */
|
|
173
300
|
get rootFeatureRefs() {
|
|
174
301
|
return this._internal.rootFeatureRefs;
|
|
175
302
|
}
|
|
176
|
-
|
|
303
|
+
/** The root features at the root of the selection tree. */
|
|
177
304
|
get features() {
|
|
178
305
|
return this._internal.features;
|
|
179
306
|
}
|
|
180
307
|
}
|
|
181
|
-
/// This method is semi-async. It will immediately give you a reference to the created
|
|
182
|
-
/// CfgProductConfiguration, but is not properly initialized until the initDone-callback
|
|
183
|
-
/// has been called.
|
|
184
|
-
CfgProductConfiguration.make = (initSuccess, initFail, rootFeatureRefs, allRawFeatures, // Flat packed. All the features that can appear anyplace in the selection tree.
|
|
185
|
-
apiSelection, parentProduct, rootProduct) => {
|
|
186
|
-
const internal = _CfgProductConfigurationInternal._makeUninitialized(rootFeatureRefs, allRawFeatures, parentProduct, rootProduct);
|
|
187
|
-
const external = new CfgProductConfiguration(internal);
|
|
188
|
-
// Note the async-callback at the end of the following line. The call is async.
|
|
189
|
-
internal
|
|
190
|
-
.setApiSelection(apiSelection, false)
|
|
191
|
-
.then(() => initSuccess(external))
|
|
192
|
-
.catch((e) => initFail(toError(e)));
|
|
193
|
-
return external;
|
|
194
|
-
};
|
|
195
|
-
/// Makes an object wrapping the passed object. This is not a clone method,
|
|
196
|
-
/// it is a method to make a new outer reference. Like a shallow copy.
|
|
197
|
-
/// We use this to help frameworks that are built around using equals to detect change.
|
|
198
|
-
CfgProductConfiguration._makeNewRefFrom = (internal) => new CfgProductConfiguration(internal);
|
|
@@ -4,7 +4,15 @@ import { CfgMtrlApplication } from "../material/CfgMtrlApplication.js";
|
|
|
4
4
|
import { CfgFeature } from "./CfgFeature.js";
|
|
5
5
|
import { _CfgOptionInternal } from "./CfgOption.js";
|
|
6
6
|
import { CfgProductConfiguration, _CfgProductConfigurationInternal } from "./CfgProductConfiguration.js";
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Returns a new array of CfgFeatures that maps to the newFeatureRefs array. Uses CfgFeatures from
|
|
9
|
+
* currentFeatures if they can be found, otherwise makes new.
|
|
10
|
+
*/
|
|
11
|
+
export declare function syncCfgFeatures(newFeatureRefs: FeatureRef[], currentFeatures: CfgFeature[], allRawFeatures: Feature[], parent: _CfgProductConfigurationInternal | _CfgOptionInternal, parentConfiguration: _CfgProductConfigurationInternal, parentProduct: _CfgProductInternal, rootProduct: _CfgProductInternal): CfgFeature[];
|
|
8
12
|
export declare function getMtrlPreview(mtrlApplications: CfgMtrlApplication[] | undefined): string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Recursively find all additional product references given a product configuration.
|
|
15
|
+
* Only selected options are considered.
|
|
16
|
+
*/
|
|
9
17
|
export declare function collectAdditionalProductRefs(parent: CfgProductConfiguration): AdditionalProductRef[];
|
|
10
18
|
//# sourceMappingURL=utilitiesProductConfiguration.d.ts.map
|
|
@@ -2,9 +2,11 @@ import { someMatch } from "@configura/web-utilities";
|
|
|
2
2
|
import { CfgFeature } from "./CfgFeature.js";
|
|
3
3
|
import { CfgOption } from "./CfgOption.js";
|
|
4
4
|
import { CfgProductConfiguration, } from "./CfgProductConfiguration.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Returns a new array of CfgFeatures that maps to the newFeatureRefs array. Uses CfgFeatures from
|
|
7
|
+
* currentFeatures if they can be found, otherwise makes new.
|
|
8
|
+
*/
|
|
9
|
+
export function syncCfgFeatures(newFeatureRefs, currentFeatures, allRawFeatures, parent, parentConfiguration, parentProduct, rootProduct) {
|
|
8
10
|
const usedRawFeatures = newFeatureRefs
|
|
9
11
|
.map((r) => r.code)
|
|
10
12
|
.map((c) => {
|
|
@@ -17,18 +19,33 @@ export function syncCfgFeatures(newFeatureRefs, currentFeatures, allRawFeatures,
|
|
|
17
19
|
const hasDuplicateDescription = someMatch(usedRawFeatures, (l, r) => {
|
|
18
20
|
return l.description.toLowerCase() === r.description.toLowerCase();
|
|
19
21
|
});
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
const newFeatures = [];
|
|
23
|
+
for (const f of usedRawFeatures) {
|
|
24
|
+
const fCode = f.code;
|
|
25
|
+
const fDescription = f.description;
|
|
26
|
+
// It is possible in Cat Creator to add the same Feature twice
|
|
27
|
+
// to the root of a Product. This fills no apparent purpose,
|
|
28
|
+
// and in CET the seem to behave as if they were the same.
|
|
29
|
+
// Removing the duplicates causes sync issues where the server
|
|
30
|
+
// expects all copies, and hence we short circuit them here
|
|
31
|
+
// by letting them refer to the same object instead.
|
|
32
|
+
const twin = newFeatures.find((f) => f.code === fCode);
|
|
33
|
+
if (twin !== undefined) {
|
|
34
|
+
newFeatures.push(twin);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
22
37
|
// Description based code helps when switching between
|
|
23
38
|
// products with similar feature-options tree and trying
|
|
24
39
|
// to retain made selections
|
|
25
|
-
const key =
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
|
|
40
|
+
const key = fDescription + (fDescription === "" || hasDuplicateDescription ? fCode : "");
|
|
41
|
+
const existingFeature = currentFeatures.find((cfgF) => cfgF.code === fCode && cfgF.key === key);
|
|
42
|
+
if (existingFeature !== undefined) {
|
|
43
|
+
newFeatures.push(existingFeature);
|
|
44
|
+
continue;
|
|
29
45
|
}
|
|
30
|
-
|
|
31
|
-
}
|
|
46
|
+
newFeatures.push(CfgFeature.make(f, allRawFeatures, key, parent, parentConfiguration, parentProduct, rootProduct));
|
|
47
|
+
}
|
|
48
|
+
return newFeatures;
|
|
32
49
|
}
|
|
33
50
|
export function getMtrlPreview(mtrlApplications) {
|
|
34
51
|
if (mtrlApplications === undefined) {
|
|
@@ -40,8 +57,10 @@ export function getMtrlPreview(mtrlApplications) {
|
|
|
40
57
|
}
|
|
41
58
|
return first.previewUrl;
|
|
42
59
|
}
|
|
43
|
-
|
|
44
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Recursively find all additional product references given a product configuration.
|
|
62
|
+
* Only selected options are considered.
|
|
63
|
+
*/
|
|
45
64
|
export function collectAdditionalProductRefs(parent) {
|
|
46
65
|
function c(parent) {
|
|
47
66
|
const result = [];
|
package/dist/productLoader.d.ts
CHANGED
|
@@ -6,7 +6,29 @@ export declare type ProductLoader = {
|
|
|
6
6
|
getProduct: GetProduct;
|
|
7
7
|
postValidate: PostValidate;
|
|
8
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* Wraps a getProduct function so that it caches for the time it lives.
|
|
11
|
+
*
|
|
12
|
+
* It has no cache invalidation or timeouts, so whatever ends up in the cache stays there
|
|
13
|
+
* indefinitely. For this reason we recommend using this with caution.
|
|
14
|
+
*
|
|
15
|
+
* In our Example App we use it to cache the internal calls in additional products as in such a
|
|
16
|
+
* product you tend to frequently get calls to same sub-product. Such as loading the same table leg
|
|
17
|
+
* four times.
|
|
18
|
+
*/
|
|
9
19
|
export declare function wrapWithGetProductCache(getProduct: GetProduct): GetProduct;
|
|
20
|
+
/**
|
|
21
|
+
* Wraps a postValidate function so that it caches for the time it lives.
|
|
22
|
+
*
|
|
23
|
+
* It has no cache invalidation or timeouts, so whatever ends up in the cache stays there
|
|
24
|
+
* indefinitely. We recommend you do not use this unless you are really, really sure what you are
|
|
25
|
+
* doing.
|
|
26
|
+
*
|
|
27
|
+
* The SDK uses it for additional products to avoid validating for example the same table legs four
|
|
28
|
+
* times at the same time. There we make sure the this cache only stays in scope for one user
|
|
29
|
+
* interaction. No longer than the actions caused by a user clicking an option.
|
|
30
|
+
*/
|
|
10
31
|
export declare function wrapWithPostValidateCache(postValidate: PostValidate): PostValidate;
|
|
32
|
+
/** Does both wrapWithGetProductCache and wrapWithPostValidateCache. */
|
|
11
33
|
export declare function wrapWithCache(loader: ProductLoader): ProductLoader;
|
|
12
34
|
//# sourceMappingURL=productLoader.d.ts.map
|
package/dist/productLoader.js
CHANGED
|
@@ -9,30 +9,38 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { PromiseCache } from "@configura/web-utilities";
|
|
11
11
|
import { makeProductKey, makeSelOptionsKey, } from "./utilitiesCatalogueData.js";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Wraps a getProduct function so that it caches for the time it lives.
|
|
14
|
+
*
|
|
15
|
+
* It has no cache invalidation or timeouts, so whatever ends up in the cache stays there
|
|
16
|
+
* indefinitely. For this reason we recommend using this with caution.
|
|
17
|
+
*
|
|
18
|
+
* In our Example App we use it to cache the internal calls in additional products as in such a
|
|
19
|
+
* product you tend to frequently get calls to same sub-product. Such as loading the same table leg
|
|
20
|
+
* four times.
|
|
21
|
+
*/
|
|
18
22
|
export function wrapWithGetProductCache(getProduct) {
|
|
19
23
|
const cache = new PromiseCache();
|
|
20
24
|
return (params) => __awaiter(this, void 0, void 0, function* () { return cache.get(makeProductKey(params, params.partNumber), () => getProduct(params)); });
|
|
21
25
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Wraps a postValidate function so that it caches for the time it lives.
|
|
28
|
+
*
|
|
29
|
+
* It has no cache invalidation or timeouts, so whatever ends up in the cache stays there
|
|
30
|
+
* indefinitely. We recommend you do not use this unless you are really, really sure what you are
|
|
31
|
+
* doing.
|
|
32
|
+
*
|
|
33
|
+
* The SDK uses it for additional products to avoid validating for example the same table legs four
|
|
34
|
+
* times at the same time. There we make sure the this cache only stays in scope for one user
|
|
35
|
+
* interaction. No longer than the actions caused by a user clicking an option.
|
|
36
|
+
*/
|
|
29
37
|
export function wrapWithPostValidateCache(postValidate) {
|
|
30
38
|
const cache = new PromiseCache();
|
|
31
39
|
return (params, body) => __awaiter(this, void 0, void 0, function* () {
|
|
32
40
|
return cache.get(`${makeProductKey(params, params.partNumber)}-${makeSelOptionsKey(body.selOptions)}`, () => postValidate(params, body));
|
|
33
41
|
});
|
|
34
42
|
}
|
|
35
|
-
|
|
43
|
+
/** Does both wrapWithGetProductCache and wrapWithPostValidateCache. */
|
|
36
44
|
export function wrapWithCache(loader) {
|
|
37
45
|
return {
|
|
38
46
|
getProduct: wrapWithGetProductCache(loader.getProduct.bind(loader)),
|