@configura/web-api 1.5.0-alpha.0 → 1.6.1-alpha.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.
Files changed (42) hide show
  1. package/dist/CatalogueAPI.d.ts +9 -2
  2. package/dist/CatalogueAPI.js +23 -0
  3. package/dist/CfgMeasure.d.ts +2 -2
  4. package/dist/CfgMeasure.js +1 -1
  5. package/dist/CfgProduct.d.ts +50 -4
  6. package/dist/CfgProduct.js +151 -17
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.js +2 -0
  9. package/dist/productConfiguration/CfgFeature.d.ts +19 -2
  10. package/dist/productConfiguration/CfgFeature.js +57 -6
  11. package/dist/productConfiguration/CfgOption.d.ts +12 -1
  12. package/dist/productConfiguration/CfgOption.js +29 -2
  13. package/dist/productConfiguration/CfgProductConfiguration.d.ts +4 -2
  14. package/dist/productConfiguration/CfgProductConfiguration.js +17 -0
  15. package/dist/productLoader.d.ts +1 -1
  16. package/dist/syncGroups/SyncGroupsApplier.d.ts +20 -0
  17. package/dist/syncGroups/SyncGroupsApplier.js +519 -0
  18. package/dist/syncGroups/SyncGroupsApplyMode.d.ts +15 -0
  19. package/dist/syncGroups/SyncGroupsApplyMode.js +15 -0
  20. package/dist/syncGroups/SyncGroupsHandler.d.ts +30 -0
  21. package/dist/syncGroups/SyncGroupsHandler.js +71 -0
  22. package/dist/syncGroups/SyncGroupsState.d.ts +20 -0
  23. package/dist/syncGroups/SyncGroupsState.js +61 -0
  24. package/dist/syncGroups/SyncGroupsTransaction.d.ts +50 -0
  25. package/dist/syncGroups/SyncGroupsTransaction.js +106 -0
  26. package/dist/tasks/TaskHandler.js +4 -3
  27. package/dist/tasks/formats.d.ts +1 -3
  28. package/dist/tasks/formats.js +3 -4
  29. package/dist/tests/testData/dummyProductForTest.d.ts +2 -2
  30. package/dist/tests/testData/dummyProductForTest.js +1 -1
  31. package/dist/tests/testData/testDataAdditionalProductInAdditionalProductInProductForTest.d.ts +3 -3
  32. package/dist/tests/testData/testDataAdditionalProductInAdditionalProductInProductForTest.js +27 -98
  33. package/dist/tests/testData/testDataCachedGetProduct.js +8 -19
  34. package/dist/tests/testData/testDataOptions.d.ts +13 -0
  35. package/dist/tests/testData/testDataOptions.js +60 -0
  36. package/dist/tests/testData/testDataProductAggregatedPrice.d.ts +3 -3
  37. package/dist/tests/testData/testDataProductAggregatedPrice.js +14 -25
  38. package/dist/tests/testData/testDataUpcharge.js +16 -48
  39. package/dist/utilitiesCatalogueData.d.ts +7 -1
  40. package/dist/utilitiesCatalogueData.js +102 -0
  41. package/dist/utilitiesNumericValues.d.ts +1 -1
  42. package/package.json +3 -3
@@ -80,8 +80,9 @@ export interface ErrorResponse {
80
80
  code: number;
81
81
  eventId?: string;
82
82
  }
83
- /** ExportFormat - The values must be the file extension used by each format. */
84
- export declare type ExportFormat = "fbx" | "dwg" | "cmdrw" | "cmfav" | "cmsym";
83
+ /** ExportFormat - Format of the exported product, identical to the format's file extension. */
84
+ export declare type ExportFormat = "glb" | "gltf" | "fbx" | "dwg" | "cmdrw" | "cmfav" | "cmsym";
85
+ export declare const exportFormatNames: ExportFormat[];
85
86
  /** ExportRequest */
86
87
  export interface ExportRequest {
87
88
  format: ExportFormat;
@@ -102,6 +103,7 @@ export interface ExportStatus {
102
103
  }
103
104
  /** ExportStatusStatus - WIP */
104
105
  export declare type ExportStatusStatus = "pending" | "running" | "finished" | "failed";
106
+ export declare const exportStatusStatusNames: ExportStatusStatus[];
105
107
  /** Feature */
106
108
  export interface Feature {
107
109
  code: string;
@@ -338,6 +340,8 @@ export interface ProductData {
338
340
  depth?: string;
339
341
  description?: string;
340
342
  height?: string;
343
+ hideIfMainProduct?: boolean;
344
+ hideIfAdditionalProduct?: boolean;
341
345
  length?: string;
342
346
  mtrlApplications?: Array<MtrlApplication>;
343
347
  additionalProductRefs?: Array<AdditionalProductRef>;
@@ -379,6 +383,7 @@ export interface RefreshSessionTokenResponse {
379
383
  }
380
384
  /** RenderFormat */
381
385
  export declare type RenderFormat = "jpg" | "png";
386
+ export declare const renderFormatNames: RenderFormat[];
382
387
  /** RenderRequest */
383
388
  export interface RenderRequest {
384
389
  width: number;
@@ -402,6 +407,7 @@ export interface RenderStatus {
402
407
  }
403
408
  /** RenderStatusStatus - WIP */
404
409
  export declare type RenderStatusStatus = "pending" | "running" | "finished" | "failed";
410
+ export declare const renderStatusStatusNames: RenderStatusStatus[];
405
411
  /** SelectedOption */
406
412
  export interface SelectedOption {
407
413
  code: string;
@@ -422,6 +428,7 @@ export interface SyncGroup {
422
428
  }
423
429
  /** SyncGroupMethods */
424
430
  export declare type SyncGroupMethods = "pull" | "push" | "twoWay";
431
+ export declare const syncGroupMethodsNames: SyncGroupMethods[];
425
432
  /** TargetCameraArgs */
426
433
  export interface TargetCameraArgs {
427
434
  location?: Point;
@@ -9,6 +9,29 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  step((generator = generator.apply(thisArg, _arguments || [])).next());
10
10
  });
11
11
  };
12
+ export const exportFormatNames = [
13
+ "glb",
14
+ "gltf",
15
+ "fbx",
16
+ "dwg",
17
+ "cmdrw",
18
+ "cmfav",
19
+ "cmsym",
20
+ ];
21
+ export const exportStatusStatusNames = [
22
+ "pending",
23
+ "running",
24
+ "finished",
25
+ "failed",
26
+ ];
27
+ export const renderFormatNames = ["jpg", "png"];
28
+ export const renderStatusStatusNames = [
29
+ "pending",
30
+ "running",
31
+ "finished",
32
+ "failed",
33
+ ];
34
+ export const syncGroupMethodsNames = ["pull", "push", "twoWay"];
12
35
  export class APIError extends Error {
13
36
  }
14
37
  export class CatalogueAPI {
@@ -1,4 +1,4 @@
1
- import { Measurement, ValueWithUnit } from "./CatalogueAPI";
1
+ import { Measurement, ValueWithUnit } from "./CatalogueAPI.js";
2
2
  /**
3
3
  * Measures can be used to Anchor items (Models or Additional Products).
4
4
  *
@@ -19,7 +19,7 @@ export declare type CfgMeasurePriority = {
19
19
  };
20
20
  /**
21
21
  * This class strips the Measurement of things that are not actually used or relevant
22
- * to Stage, specifically anchoring and stretching. Measurments can also contain
22
+ * to Stage, specifically anchoring and stretching. Measurements can also contain
23
23
  * volume and area, but these are ignored. Here Measures are used to define defaults
24
24
  * such as its preferences when dealing with ambiguity or its default value.
25
25
  */
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * This class strips the Measurement of things that are not actually used or relevant
3
- * to Stage, specifically anchoring and stretching. Measurments can also contain
3
+ * to Stage, specifically anchoring and stretching. Measurements can also contain
4
4
  * volume and area, but these are ignored. Here Measures are used to define defaults
5
5
  * such as its preferences when dealing with ambiguity or its default value.
6
6
  */
@@ -2,9 +2,11 @@ import { AggregatedLoadingObservable, LengthUnit, Observable, SingleArgCallback
2
2
  import { AdditionalProductConfiguration, CatalogueParams, MeasureParam, MtrlApplication, Prices, Transform } from "./CatalogueAPI.js";
3
3
  import { CfgMeasureDefinition } from "./CfgMeasure.js";
4
4
  import { _CfgFeatureInternal } from "./productConfiguration/CfgFeature.js";
5
- import { ProductConfigurationBubbleMode } from "./productConfiguration/CfgOption.js";
6
- import { CfgProductConfiguration } from "./productConfiguration/CfgProductConfiguration.js";
5
+ import { ProductConfigurationBubbleMode, _CfgOptionInternal } from "./productConfiguration/CfgOption.js";
6
+ import { CfgProductConfiguration, _CfgProductConfigurationInternal } from "./productConfiguration/CfgProductConfiguration.js";
7
7
  import { ProductLoader } from "./productLoader.js";
8
+ import { SyncGroupsApplyMode } from "./syncGroups/SyncGroupsApplyMode.js";
9
+ import { SyncGroupsHandler } from "./syncGroups/SyncGroupsHandler.js";
8
10
  import { CfgProductData, RootNodeSource } from "./utilitiesCatalogueData.js";
9
11
  export declare type CfgProductChangeNotification = {
10
12
  freshRef: CfgProduct;
@@ -24,6 +26,12 @@ export declare type CfgProductSettings = {
24
26
  * believe is a rare use case.
25
27
  */
26
28
  strictSetApiSelectionMatch: boolean;
29
+ /**
30
+ * Controls if SyncGroups are applied Fast or Strict. Fast keeps the number of validate calls
31
+ * to the server down, Strict is closer to how CET works. We recommend you use Fast as cases
32
+ * where the results differ should be rare in real use cases.
33
+ */
34
+ syncGroupsApplyMode: SyncGroupsApplyMode | undefined;
27
35
  };
28
36
  /**
29
37
  * This enum is used internally in the SDK and is not expected by be used directly by integrators.
@@ -48,6 +56,12 @@ export declare type CfgPrice = {
48
56
  currency: string;
49
57
  fractionDigits: number;
50
58
  };
59
+ export declare type CfgPathSegment = string;
60
+ export declare type CfgPath = CfgPathSegment[];
61
+ export declare type RevalidateResult = {
62
+ wasAborted: boolean;
63
+ requestDidValidate: boolean;
64
+ };
51
65
  /**
52
66
  * This class is meant to only be used through CfgProduct. It should never be instantiated on its
53
67
  * own. Normally the internal state of this class should never be directly modified. CfgProduct is
@@ -68,6 +82,7 @@ export declare class _CfgProductInternal {
68
82
  readonly parent: _CfgProductInternal | undefined;
69
83
  readonly transform: Transform | undefined;
70
84
  anchor: MeasureParam | undefined;
85
+ private readonly _syncGroupHandler;
71
86
  static make: (productLoaderRaw: ProductLoader, productLoaderForGroupedLoad: ProductLoader | undefined, lang: string, catId: CatalogueParams, partNumber: string, settings: CfgProductSettings, optional: boolean, loadingObservable: AggregatedLoadingObservable, refKey: string | undefined, refDescription: string | undefined, parent: _CfgProductInternal | undefined, root: _CfgProductInternal | undefined, transform: Transform | undefined, anchor: MeasureParam | undefined) => Promise<_CfgProductInternal>;
72
87
  private constructor();
73
88
  readonly root: _CfgProductInternal;
@@ -97,6 +112,21 @@ export declare class _CfgProductInternal {
97
112
  setSelected(v: boolean, bubbleMode: CfgProductBubbleMode): Promise<boolean>;
98
113
  get configuration(): CfgProductConfiguration;
99
114
  get rawProductData(): CfgProductData;
115
+ /**
116
+ * Please note that this relates to the visibility in the Configuration tree.
117
+ * It does not affect the visibility of anything in the 3D view at all.
118
+ */
119
+ get visibleIfAdditionalProduct(): boolean;
120
+ /**
121
+ * Please note that this relates to the visibility in the Configuration tree.
122
+ * It does not affect the visibility of anything in the 3D view at all.
123
+ */
124
+ get visibleIfMainProduct(): boolean;
125
+ /**
126
+ * Please note that this relates to the visibility in the Configuration tree.
127
+ * It does not affect the visibility of anything in the 3D view at all.
128
+ */
129
+ get visible(): boolean;
100
130
  _notifyAllOfChange: (bubbleMode: CfgProductBubbleMode) => Promise<void>;
101
131
  /** Called when a child (additional product or the configuration) has changed. */
102
132
  private _childHasChanged;
@@ -106,6 +136,15 @@ export declare class _CfgProductInternal {
106
136
  _configurationHasChanged: (freshRef: CfgProductConfiguration, bubbleMode: ProductConfigurationBubbleMode) => Promise<void>;
107
137
  getApiSelection: () => AdditionalProductConfiguration;
108
138
  setApiSelection: (s: AdditionalProductConfiguration, doValidate: boolean, productLoaderForGroupedLoad?: ProductLoader | undefined) => Promise<boolean>;
139
+ copyFrom: (otherProduct: _CfgProductInternal, doValidate: boolean, productLoaderForGroupedLoad?: ProductLoader | undefined) => Promise<boolean>;
140
+ private _setApiSelectionWithOtherProduct;
141
+ get syncGroupHandler(): SyncGroupsHandler | undefined;
142
+ get path(): CfgPath;
143
+ getFromPath(path: CfgPath): _CfgProductInternal | _CfgProductConfigurationInternal | _CfgFeatureInternal | _CfgOptionInternal;
144
+ getProductFromPath(path: CfgPath): _CfgProductInternal;
145
+ getProductConfigurationFromPath(path: CfgPath): _CfgProductConfigurationInternal;
146
+ getFeatureFromPath(path: CfgPath): _CfgFeatureInternal;
147
+ getOptionFromPath(path: CfgPath): _CfgOptionInternal;
109
148
  structureCompare: (other: _CfgProductInternal, strictOrder?: boolean, descriptionMatch?: boolean) => boolean;
110
149
  tryMatchSelection: (other: _CfgProductInternal, descriptionMatch?: boolean, productLoaderForGroupedLoad?: ProductLoader | undefined) => Promise<boolean>;
111
150
  /** Only features in selected options and selected additional products. */
@@ -116,7 +155,7 @@ export declare class _CfgProductInternal {
116
155
  * product in isolation. The validation result is applied on the configuration. Then additional
117
156
  * products are synced (unloaded, loaded etc.) Finally the changes bubble up the tree.
118
157
  */
119
- _revalidate: (bubbleMode: CfgProductBubbleMode, productLoader: ProductLoader) => Promise<boolean>;
158
+ _revalidate: (bubbleMode: CfgProductBubbleMode, productLoader: ProductLoader) => Promise<RevalidateResult>;
120
159
  /**
121
160
  * Based on this configuration find what additional products should be shown and not, unload
122
161
  * (i.e. destroy) those that should no longer be shown, load the new ones.
@@ -128,7 +167,7 @@ export declare class CfgProduct {
128
167
  static make(productLoader: ProductLoader, lang: string, catId: CatalogueParams, partNumber: string, settings?: Partial<CfgProductSettings>): Promise<CfgProduct>;
129
168
  /**
130
169
  * Makes an object wrapping the passed object. This is not a clone method, it is a method to
131
- * make a new outer reference. Like a shallow copy./ We use this to help frameworks that are
170
+ * make a new outer reference. Like a shallow copy. We use this to help frameworks that are
132
171
  * build around using equals to detect change.
133
172
  */
134
173
  static _makeNewRefFrom(source: _CfgProductInternal): CfgProduct;
@@ -163,6 +202,13 @@ export declare class CfgProduct {
163
202
  * always true.
164
203
  */
165
204
  get selected(): boolean;
205
+ /**
206
+ * Please note that this relates to the visibility in the Configuration tree.
207
+ * It does not affect the visibility of anything in the 3D view at all.
208
+ * Visibility is affects the Configuration for this Product, but any Additional Products
209
+ * will not be affected.
210
+ */
211
+ get visible(): boolean;
166
212
  /**
167
213
  * Only applicable when this product is optional.
168
214
  * Setting this does not cause a validation call as toggling an optional additional product is
@@ -9,16 +9,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { AggregatedLoadingObservable, compareArrays, count, Observable, toLengthUnit, } from "@configura/web-utilities";
11
11
  import { CfgMeasureDefinition } from "./CfgMeasure.js";
12
- import { ProductConfigurationBubbleMode } from "./productConfiguration/CfgOption.js";
13
- import { CfgProductConfiguration } from "./productConfiguration/CfgProductConfiguration.js";
12
+ import { _CfgFeatureInternal } from "./productConfiguration/CfgFeature.js";
13
+ import { ProductConfigurationBubbleMode, _CfgOptionInternal, } from "./productConfiguration/CfgOption.js";
14
+ import { CfgProductConfiguration, _CfgProductConfigurationInternal, } from "./productConfiguration/CfgProductConfiguration.js";
14
15
  import { collectAdditionalProductRefs } from "./productConfiguration/utilitiesProductConfiguration.js";
15
16
  import { wrapWithCache } from "./productLoader.js";
16
- import { comparePricesObjects, correctDefaultsOnCatalogueParams, makeProductKey, } from "./utilitiesCatalogueData.js";
17
+ import { SyncGroupsHandler } from "./syncGroups/SyncGroupsHandler.js";
18
+ import { comparePricesObjects, correctDefaultsOnCatalogueParams, isSameProductRef, makeProductKey, } from "./utilitiesCatalogueData.js";
17
19
  function completeSettings(incompleteSettings) {
18
20
  var _a, _b;
19
21
  return {
20
22
  strictSelectOneSelectionCount: (_a = incompleteSettings === null || incompleteSettings === void 0 ? void 0 : incompleteSettings.strictSelectOneSelectionCount) !== null && _a !== void 0 ? _a : false,
21
23
  strictSetApiSelectionMatch: (_b = incompleteSettings === null || incompleteSettings === void 0 ? void 0 : incompleteSettings.strictSetApiSelectionMatch) !== null && _b !== void 0 ? _b : false,
24
+ syncGroupsApplyMode: incompleteSettings === null || incompleteSettings === void 0 ? void 0 : incompleteSettings.syncGroupsApplyMode,
22
25
  };
23
26
  }
24
27
  /**
@@ -50,7 +53,7 @@ function isDescriptionMatch(l, r) {
50
53
  * the class that should be used and interacted with.
51
54
  */
52
55
  export class _CfgProductInternal {
53
- constructor(initSuccess, initFail, _productLoaderRaw, lang, catId, partNumber, settings, optional, selected, rootFeatureRefs, allRawFeatures, uuid, _rawUnit, _rawProductData, apiSelection, loadingObservable, refKey, refDescription, parent, root, transform, anchor) {
56
+ constructor(initSuccess, initFail, _productLoaderRaw, lang, catId, partNumber, settings, optional, selected, rootFeatureRefs, allRawFeatures, uuid, _rawUnit, _rawProductData, apiSelection, loadingObservable, refKey, refDescription, parent, root, transform, anchor, _syncGroupHandler) {
54
57
  this._productLoaderRaw = _productLoaderRaw;
55
58
  this.lang = lang;
56
59
  this.catId = catId;
@@ -65,6 +68,7 @@ export class _CfgProductInternal {
65
68
  this.parent = parent;
66
69
  this.transform = transform;
67
70
  this.anchor = anchor;
71
+ this._syncGroupHandler = _syncGroupHandler;
68
72
  this._destroyed = false;
69
73
  this.additionalProducts = [];
70
74
  this.changeObservable = new Observable();
@@ -120,6 +124,9 @@ export class _CfgProductInternal {
120
124
  // The revalidate call will continue the bubble
121
125
  yield this._revalidate(CfgProductBubbleMode.ToRootAndBubbleSelected, this._productLoaderRaw);
122
126
  return;
127
+ case ProductConfigurationBubbleMode.BubbleSelected:
128
+ yield this._childHasChanged(CfgProductBubbleMode.ToRootAndBubbleSelected);
129
+ return;
123
130
  case ProductConfigurationBubbleMode.Validate:
124
131
  // The revalidate call will continue the bubble
125
132
  yield this._revalidate(CfgProductBubbleMode.ToRoot, this._productLoaderRaw);
@@ -145,13 +152,26 @@ export class _CfgProductInternal {
145
152
  additionalProducts: this.additionalProducts.map((additionalProduct) => additionalProduct.getApiSelection()),
146
153
  });
147
154
  this.setApiSelection = (s, doValidate, productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
155
+ return this._setApiSelectionWithOtherProduct(s, doValidate, productLoaderForGroupedLoad, undefined);
156
+ });
157
+ this.copyFrom = (otherProduct, doValidate, productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
158
+ return yield this._setApiSelectionWithOtherProduct(otherProduct.getApiSelection(), doValidate, productLoaderForGroupedLoad, otherProduct);
159
+ });
160
+ this._setApiSelectionWithOtherProduct = (s, doValidate, productLoaderForGroupedLoad, sourceProduct) => __awaiter(this, void 0, void 0, function* () {
148
161
  // Wrap with cache will make getProduct for this function call use the same server call
149
162
  // for the same product with the same params. Used for getProduct (when a new additional
150
163
  // product is loaded) and postValidate.
151
164
  productLoaderForGroupedLoad =
152
165
  productLoaderForGroupedLoad || wrapWithCache(this._productLoaderRaw);
166
+ let change = false;
167
+ if (sourceProduct !== undefined) {
168
+ this._rawProductData = sourceProduct.rawProductData;
169
+ change = true; // We can not know if this is an actual change, so we assume it is
170
+ }
153
171
  const configurationChange = yield this.configuration._internal.setApiSelection(s.selOptions, false);
154
- let change = configurationChange;
172
+ if (configurationChange) {
173
+ change = true;
174
+ }
155
175
  if (this.optional) {
156
176
  if (yield this.setSelected(s.selected !== false, CfgProductBubbleMode.Stop)) {
157
177
  change = true;
@@ -165,8 +185,14 @@ export class _CfgProductInternal {
165
185
  if (apiSelectionAdditionalProductsCount !== additionalProductsCount) {
166
186
  throw new Error(`Additional products are not same length. This product: "${this.key}". This product additional products count: ${additionalProductsCount}. Passed apiSelection additional products count: ${apiSelectionAdditionalProductsCount}`);
167
187
  }
168
- if ((yield Promise.all(apiSelectionAdditionalProducts.map((apiSelectionChild) => {
169
- const refKey = apiSelectionChild.refKey;
188
+ const sourceProductAdditionalProducts = sourceProduct === null || sourceProduct === void 0 ? void 0 : sourceProduct.additionalProducts;
189
+ if (sourceProductAdditionalProducts &&
190
+ additionalProductsCount !== sourceProductAdditionalProducts.length) {
191
+ throw new Error(`Passed sourceProduct does not have the same number of additional products as this.`);
192
+ }
193
+ if ((yield Promise.all(apiSelectionAdditionalProducts.map((apiSelectionAdditionalProduct, index) => {
194
+ var _a;
195
+ const refKey = apiSelectionAdditionalProduct.refKey;
170
196
  if (refKey === undefined) {
171
197
  throw new Error("Additional product api configurations must have refKey.");
172
198
  }
@@ -174,8 +200,16 @@ export class _CfgProductInternal {
174
200
  if (i === -1) {
175
201
  throw new Error(`Additional product not found. This product: "${this.key}". refKey of not found additional product: "${refKey}"`);
176
202
  }
203
+ let sourceProductAdditionalProduct = undefined;
204
+ if (sourceProductAdditionalProducts !== undefined) {
205
+ sourceProductAdditionalProduct =
206
+ (_a = sourceProductAdditionalProducts.find((a) => refKey === a.refKey)) === null || _a === void 0 ? void 0 : _a._internal;
207
+ if (sourceProductAdditionalProduct === undefined) {
208
+ throw new Error("Additional product not found in sourceProduct");
209
+ }
210
+ }
177
211
  const additionalProduct = additionalProducts.splice(i, 1)[0]; // Splicing like this is okay because this is done synchronous. The setCon. is what is async.
178
- return additionalProduct._internal.setApiSelection(apiSelectionChild, doValidate, productLoaderForGroupedLoad);
212
+ return additionalProduct._internal._setApiSelectionWithOtherProduct(apiSelectionAdditionalProduct, doValidate, productLoaderForGroupedLoad, sourceProductAdditionalProduct);
179
213
  }))).some((b) => b)) {
180
214
  change = true;
181
215
  }
@@ -270,6 +304,7 @@ export class _CfgProductInternal {
270
304
  this._revalidateInProgressToken = token;
271
305
  try {
272
306
  const response = yield productLoader.postValidate(Object.assign(Object.assign({ lang: this.lang }, correctDefaultsOnCatalogueParams(this.catId)), { partNumber: this.partNumber }), { selOptions: configuration.getApiSelection() });
307
+ const requestDidValidate = response.validated;
273
308
  // The revalidateInProgressToken is used to know if some other revalidate
274
309
  // call has happened after this call, thereby making this call obsolete.
275
310
  // This is a bit crude in that it does not actually cancel previous validate
@@ -278,7 +313,7 @@ export class _CfgProductInternal {
278
313
  // of all, the heavy work happens on the server, and that work will not be
279
314
  // cancelled even if we would cancel the call.
280
315
  if (this._revalidateInProgressToken !== token) {
281
- return false;
316
+ return { wasAborted: true, requestDidValidate };
282
317
  }
283
318
  // After a successful validate-call we will always assume there
284
319
  // is a change. It would be possible to compare relevant parts
@@ -286,7 +321,7 @@ export class _CfgProductInternal {
286
321
  // syndAndLoad, however the code comparing productData would be fragile
287
322
  // and likely to break if new data-fields were added.
288
323
  if (this._destroyed) {
289
- return false;
324
+ return { wasAborted: true, requestDidValidate };
290
325
  }
291
326
  const { productData, rootFeatureRefs } = response;
292
327
  const pricesUpdated = !comparePricesObjects(this.prices, productData.partsData.prices);
@@ -301,10 +336,10 @@ export class _CfgProductInternal {
301
336
  yield configuration._internal.setApiSelection(productData.partsData.selOptions || [], false);
302
337
  yield this._syncAndLoadAdditionalProducts(productLoader);
303
338
  if (this._destroyed) {
304
- return false;
339
+ return { wasAborted: true, requestDidValidate };
305
340
  }
306
341
  yield this._notifyAllOfChange(bubbleMode);
307
- return true;
342
+ return { wasAborted: false, requestDidValidate };
308
343
  }
309
344
  catch (e) {
310
345
  throw e;
@@ -322,7 +357,14 @@ export class _CfgProductInternal {
322
357
  const additionalProductRefs = [
323
358
  ...(productData.additionalProductRefs || []),
324
359
  ...collectAdditionalProductRefs(configuration),
325
- ].map((prodRef, originalIndex) => ({
360
+ ]
361
+ .reduce((a, c) => {
362
+ if (a.every((p) => !isSameProductRef(p, c))) {
363
+ a.push(c);
364
+ }
365
+ return a;
366
+ }, [])
367
+ .map((prodRef, originalIndex) => ({
326
368
  prodRef,
327
369
  originalIndex,
328
370
  }));
@@ -368,7 +410,7 @@ export class _CfgProductInternal {
368
410
  this.loadingObservable.stopChildLoading(token);
369
411
  }
370
412
  });
371
- this.root = root || this;
413
+ this.root = root !== null && root !== void 0 ? root : this;
372
414
  this.key = makeProductKey(catId, refKey || partNumber);
373
415
  this._selected = optional ? selected : undefined;
374
416
  this.isAdditionalProduct = parent !== undefined;
@@ -380,9 +422,10 @@ export class _CfgProductInternal {
380
422
  clone(parent, root) {
381
423
  return __awaiter(this, void 0, void 0, function* () {
382
424
  const product = yield new Promise((initSuccess, initFail) => {
425
+ var _a;
383
426
  const p = new _CfgProductInternal(() => {
384
427
  initSuccess(p);
385
- }, initFail, this._productLoaderRaw, this.lang, this.catId, this.partNumber, this.settings, this.optional, this.selected, this._configuration.rootFeatureRefs, this._configuration.allRawFeatures, this.uuid, this._rawUnit, this._rawProductData, this.configuration.getApiSelection(), new AggregatedLoadingObservable(), this.refKey, this.refDescription, parent, root, this.transform, this.anchor);
428
+ }, initFail, this._productLoaderRaw, this.lang, this.catId, this.partNumber, this.settings, this.optional, this.selected, this._configuration.rootFeatureRefs, this._configuration.allRawFeatures, this.uuid, this._rawUnit, this._rawProductData, this.configuration.getApiSelection(), new AggregatedLoadingObservable(), this.refKey, this.refDescription, parent, root, this.transform, this.anchor, (_a = this._syncGroupHandler) === null || _a === void 0 ? void 0 : _a.clone());
386
429
  });
387
430
  for (const additionalProduct of this.additionalProducts) {
388
431
  product.additionalProducts.push(CfgProduct._makeNewRefFrom(yield additionalProduct._internal.clone(product, root || product)));
@@ -471,6 +514,85 @@ export class _CfgProductInternal {
471
514
  get rawProductData() {
472
515
  return this._rawProductData;
473
516
  }
517
+ /**
518
+ * Please note that this relates to the visibility in the Configuration tree.
519
+ * It does not affect the visibility of anything in the 3D view at all.
520
+ */
521
+ get visibleIfAdditionalProduct() {
522
+ return this.rawProductData.hideIfAdditionalProduct !== true;
523
+ }
524
+ /**
525
+ * Please note that this relates to the visibility in the Configuration tree.
526
+ * It does not affect the visibility of anything in the 3D view at all.
527
+ */
528
+ get visibleIfMainProduct() {
529
+ return this.rawProductData.hideIfMainProduct !== true;
530
+ }
531
+ /**
532
+ * Please note that this relates to the visibility in the Configuration tree.
533
+ * It does not affect the visibility of anything in the 3D view at all.
534
+ */
535
+ get visible() {
536
+ return this.isAdditionalProduct
537
+ ? this.visibleIfAdditionalProduct
538
+ : this.visibleIfMainProduct;
539
+ }
540
+ get syncGroupHandler() {
541
+ return this.root._syncGroupHandler;
542
+ }
543
+ get path() {
544
+ if (this.parent === undefined || this.refKey === undefined) {
545
+ return [];
546
+ }
547
+ return [...this.parent.path, "p", this.refKey];
548
+ }
549
+ getFromPath(path) {
550
+ path = path.slice();
551
+ const s = path.shift();
552
+ switch (s) {
553
+ case undefined:
554
+ return this;
555
+ case "p":
556
+ const refKey = path.shift();
557
+ const additionalProduct = this.additionalProducts.find((p) => p.refKey === refKey);
558
+ if (additionalProduct === undefined) {
559
+ throw new Error(`Additional product not found "p, ${refKey}, ${path.join(", ")}"`);
560
+ }
561
+ return additionalProduct._internal.getFromPath(path);
562
+ case "c":
563
+ return this.configuration._internal.getFromPath(path);
564
+ default:
565
+ throw new Error(`Unexpected path segment ${s}`);
566
+ }
567
+ }
568
+ getProductFromPath(path) {
569
+ const p = this.getFromPath(path);
570
+ if (p instanceof _CfgProductInternal) {
571
+ return p;
572
+ }
573
+ throw new Error(`Path did not lead to a Product "${p.path.join(", ")}"`);
574
+ }
575
+ getProductConfigurationFromPath(path) {
576
+ const c = this.getFromPath(path);
577
+ if (c instanceof _CfgProductConfigurationInternal) {
578
+ return c;
579
+ }
580
+ throw new Error(`Path did not lead to a ProductConfiguration "${c.path.join(", ")}"`);
581
+ }
582
+ getFeatureFromPath(path) {
583
+ const f = this.getFromPath(path);
584
+ if (f instanceof _CfgFeatureInternal) {
585
+ return f;
586
+ }
587
+ throw new Error(`Path did not lead to a Feature "${f.path.join(", ")}"`);
588
+ }
589
+ getOptionFromPath(path) {
590
+ const o = this.getFromPath(path);
591
+ if (o instanceof _CfgOptionInternal) {
592
+ return o;
593
+ }
594
+ throw new Error(`Path did not lead to a Option "${o.path.join(", ")}"`);
595
+ }
474
596
  }
475
597
  _CfgProductInternal.make = (productLoaderRaw, productLoaderForGroupedLoad, // Used when instantiating the current product
476
598
  lang, catId, partNumber, settings, optional, loadingObservable, refKey, refDescription, parent, root, transform, anchor) => __awaiter(void 0, void 0, void 0, function* () {
@@ -479,6 +601,7 @@ lang, catId, partNumber, settings, optional, loadingObservable, refKey, refDescr
479
601
  // at this initial load.
480
602
  productLoaderForGroupedLoad =
481
603
  productLoaderForGroupedLoad || wrapWithCache(productLoaderRaw);
604
+ const syncGroupHandler = root === undefined ? SyncGroupsHandler.make(settings.syncGroupsApplyMode) : undefined;
482
605
  const productResponse = yield productLoaderForGroupedLoad.getProduct(Object.assign(Object.assign({ lang }, correctDefaultsOnCatalogueParams(catId)), { partNumber }));
483
606
  const { productData, rootFeatureRefs, features: allRawFeatures, uuid, unit, } = productResponse;
484
607
  const product = yield new Promise((initSuccess, initFail) => {
@@ -487,9 +610,11 @@ lang, catId, partNumber, settings, optional, loadingObservable, refKey, refDescr
487
610
  // But we can not set the api selection synchronously. And the product configuration needs "this". So we use this callback.
488
611
  // Feel free to find a nicer more readable solution :)
489
612
  initSuccess(p);
490
- }, initFail, productLoaderRaw, lang, catId, partNumber, settings, optional, !optional, rootFeatureRefs, allRawFeatures, uuid, unit, productData, productData.partsData.selOptions || [], loadingObservable, refKey, refDescription, parent, root, transform, anchor);
613
+ }, initFail, productLoaderRaw, lang, catId, partNumber, settings, optional, !optional, rootFeatureRefs, allRawFeatures, uuid, unit, productData, productData.partsData.selOptions || [], loadingObservable, refKey, refDescription, parent, root, transform, anchor, syncGroupHandler);
491
614
  });
492
615
  yield product._syncAndLoadAdditionalProducts(productLoaderForGroupedLoad);
616
+ // Product is guaranteed to be root
617
+ yield (syncGroupHandler === null || syncGroupHandler === void 0 ? void 0 : syncGroupHandler.init(product, productLoaderForGroupedLoad));
493
618
  return product;
494
619
  });
495
620
  export class CfgProduct {
@@ -545,7 +670,7 @@ export class CfgProduct {
545
670
  }
546
671
  /**
547
672
  * Makes an object wrapping the passed object. This is not a clone method, it is a method to
548
- * make a new outer reference. Like a shallow copy./ We use this to help frameworks that are
673
+ * make a new outer reference. Like a shallow copy. We use this to help frameworks that are
549
674
  * build around using equals to detect change.
550
675
  */
551
676
  static _makeNewRefFrom(source) {
@@ -589,6 +714,15 @@ export class CfgProduct {
589
714
  get selected() {
590
715
  return this._internal.selected;
591
716
  }
717
+ /**
718
+ * Please note that this relates to the visibility in the Configuration tree.
719
+ * It does not affect the visibility of anything in the 3D view at all.
720
+ * Visibility is affects the Configuration for this Product, but any Additional Products
721
+ * will not be affected.
722
+ */
723
+ get visible() {
724
+ return this._internal.visible;
725
+ }
592
726
  get rawProductData() {
593
727
  return this._internal.rawProductData;
594
728
  }
package/dist/index.d.ts CHANGED
@@ -11,6 +11,8 @@ export * from "./productConfiguration/CfgProductConfiguration.js";
11
11
  export * from "./productConfiguration/filters.js";
12
12
  export * from "./productConfiguration/productParamsGenerator.js";
13
13
  export * from "./productLoader.js";
14
+ export * from "./syncGroups/SyncGroupsApplyMode.js";
15
+ export * from "./syncGroups/SyncGroupsHandler.js";
14
16
  export * from "./tasks/formats.js";
15
17
  export * from "./tasks/TaskHandler.js";
16
18
  export * from "./utilitiesCatalogueData.js";
package/dist/index.js CHANGED
@@ -11,6 +11,8 @@ export * from "./productConfiguration/CfgProductConfiguration.js";
11
11
  export * from "./productConfiguration/filters.js";
12
12
  export * from "./productConfiguration/productParamsGenerator.js";
13
13
  export * from "./productLoader.js";
14
+ export * from "./syncGroups/SyncGroupsApplyMode.js";
15
+ export * from "./syncGroups/SyncGroupsHandler.js";
14
16
  export * from "./tasks/formats.js";
15
17
  export * from "./tasks/TaskHandler.js";
16
18
  export * from "./utilitiesCatalogueData.js";
@@ -1,6 +1,6 @@
1
1
  import { LengthUnit, Observable, SingleArgCallback } from "@configura/web-utilities";
2
- import { Feature, SelectedOption } from "../CatalogueAPI.js";
3
- import { CfgProduct, _CfgProductInternal } from "../CfgProduct.js";
2
+ import { Feature, SelectedOption, SyncGroup } from "../CatalogueAPI.js";
3
+ import { CfgPath, CfgProduct, _CfgProductInternal } from "../CfgProduct.js";
4
4
  import { CfgMtrlApplication } from "../material/CfgMtrlApplication.js";
5
5
  import { CfgOption, ProductConfigurationBubbleMode, _CfgOptionInternal } from "./CfgOption.js";
6
6
  import { _CfgProductConfigurationInternal } from "./CfgProductConfiguration.js";
@@ -49,6 +49,7 @@ export declare class _CfgFeatureInternal {
49
49
  get isUseNumericValue(): boolean;
50
50
  get numericValue(): number | undefined;
51
51
  setNumericValue: (val: number) => Promise<boolean>;
52
+ get syncGroup(): SyncGroup | undefined;
52
53
  get description(): string;
53
54
  /**
54
55
  * The MeasureParam class is re-used for different purposes. In Features it is used
@@ -66,8 +67,20 @@ export declare class _CfgFeatureInternal {
66
67
  */
67
68
  private get isAllOptionsAffectedByAnySelection();
68
69
  get preview(): string | undefined;
70
+ /**
71
+ * Please note that this relates to the visibility in the Configuration tree.
72
+ * It does not affect the visibility of anything in the 3D view at all.
73
+ */
69
74
  get visibleIfAdditionalProduct(): boolean;
75
+ /**
76
+ * Please note that this relates to the visibility in the Configuration tree.
77
+ * It does not affect the visibility of anything in the 3D view at all.
78
+ */
70
79
  get visibleIfMainProduct(): boolean;
80
+ /**
81
+ * Please note that this relates to the visibility in the Configuration tree.
82
+ * It does not affect the visibility of anything in the 3D view at all.
83
+ */
71
84
  get visible(): boolean;
72
85
  get options(): CfgOption[];
73
86
  private _notifyAllOfChange;
@@ -83,6 +96,8 @@ export declare class _CfgFeatureInternal {
83
96
  setApiSelection: (apiOptionSelectionMap: {
84
97
  [index: string]: SelectedOption;
85
98
  } | undefined) => Promise<boolean>;
99
+ get path(): CfgPath;
100
+ getFromPath(path: CfgPath): _CfgFeatureInternal | _CfgOptionInternal;
86
101
  /** Pushes to refresh stretch. Does not cause validation. */
87
102
  pushStretch: () => Promise<boolean>;
88
103
  structureCompare: (other: _CfgFeatureInternal, strictOrder?: boolean, descriptionMatch?: boolean) => boolean;
@@ -148,6 +163,8 @@ export declare class CfgFeature {
148
163
  get preview(): string | undefined;
149
164
  get options(): CfgOption[];
150
165
  /**
166
+ * Please note that this relates to the visibility in the Configuration tree.
167
+ * It does not affect the visibility of anything in the 3D view at all.
151
168
  * Visibility is not inherited. If this is hidden the children
152
169
  * of this Feature might still be visible, depending on their settings.
153
170
  */