@contrail/documents 2.1.0-alpha.4325cd8 → 2.1.0-alpha.5e3a42c

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/CHANGELOG.md CHANGED
@@ -20,6 +20,31 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
20
20
  - `SECTION_ELEMENT_TYPE`, `FRAME_ELEMENT_TYPE` and `SHAPE_ELEMENT_TYPES` in
21
21
  `document-element-constants`, so the rule above has one definition rather than one per app.
22
22
 
23
+ ### Changed (breaking)
24
+
25
+ - `isContainerBinding()` becomes `DynamicTextUtil.takesValueFromContainer()`. Same rule, named for
26
+ what it decides: whether the binding's value comes from the container rather than a type property.
27
+
28
+ ### Fixed
29
+
30
+ - A dynamic text is now anchored at its position rather than its centre when deciding which
31
+ container it belongs to, and `DynamicTextUtil.isDynamicText` is the shared test for one. Its size
32
+ comes from the value it resolves to, and that value depends on the container, so letting its width
33
+ feed membership meant its own text could choose its container: near an edge the two answers
34
+ disagreed indefinitely, flipping between a value and "Move to a frame" on every read. Everything
35
+ else still resolves on its centre.
36
+ - `min` and `max` rendered an infinity sign when every member value was empty. The early return
37
+ checked the list length before the empty values were filtered out, so `Math.min` of nothing
38
+ reached the formatter. Both now fall through to `n/a`.
39
+ - `DynamicTextUtil.calculateSize` threw on an element that reached it without a `size`, which on
40
+ the server failed the read for the whole document. It now measures from a zero size instead.
41
+
42
+ ### Changed
43
+
44
+ - `DocumentElementFactory.getBoundingClientRect` multiplies the measured size by the element's
45
+ `scale`. Scale grows an element from its top-left, so it widens the box and moves the centre; a
46
+ scaled element previously measured at its unscaled size and could resolve to the wrong container.
47
+
23
48
  ## [1.17.0] - 2026-09-09
24
49
 
25
50
  ### Added
@@ -1,9 +1,10 @@
1
- import { PositionDefinition } from '../types';
1
+ import { DocumentElement, PositionDefinition } from '../types';
2
2
  import { ContainerElementType } from './container-kind';
3
3
  import { DocumentContainer } from './document-container';
4
4
  export declare class DocumentContainerIndex {
5
5
  private readonly data;
6
6
  constructor(data: {
7
+ documentElementsById: Map<string, DocumentElement>;
7
8
  containersById: Map<string, DocumentContainer>;
8
9
  containerPathsByDocumentElementId: Map<string, Map<string, string[]>>;
9
10
  memberIdsByContainerId: Map<string, string[]>;
@@ -11,11 +12,13 @@ export declare class DocumentContainerIndex {
11
12
  });
12
13
  getContainer(containerId: string): DocumentContainer | null;
13
14
  containersOf(documentElementId: string, elementType: ContainerElementType): DocumentContainer[];
14
- containerIdsOf(documentElementId: string, elementType: ContainerElementType): string[];
15
+ containerIdsOf(documentElementId: string, elementType: ContainerElementType): readonly string[];
15
16
  innermostContainerIdOf(documentElementId: string, elementType: ContainerElementType): string | null;
16
- memberIdsOf(containerId: string): string[];
17
- containerPathAtPoint(point: PositionDefinition, elementType: ContainerElementType): string[];
17
+ memberIdsOf(containerId: string): readonly string[];
18
+ memberDocumentElementsOf(containerId: string): DocumentElement[];
19
+ containerPathAtPoint(point: PositionDefinition, elementType: ContainerElementType): readonly string[];
18
20
  maxDepth(elementType: ContainerElementType): number;
19
21
  private containersOfType;
22
+ private resolveAll;
20
23
  private containerPath;
21
24
  }
@@ -11,9 +11,7 @@ class DocumentContainerIndex {
11
11
  return (_a = this.data.containersById.get(containerId)) !== null && _a !== void 0 ? _a : null;
12
12
  }
13
13
  containersOf(documentElementId, elementType) {
14
- return this.containerIdsOf(documentElementId, elementType)
15
- .map((containerId) => this.data.containersById.get(containerId))
16
- .filter(Boolean);
14
+ return this.resolveAll(this.containerIdsOf(documentElementId, elementType));
17
15
  }
18
16
  containerIdsOf(documentElementId, elementType) {
19
17
  return this.containerPath(documentElementId, elementType);
@@ -26,6 +24,16 @@ class DocumentContainerIndex {
26
24
  var _a;
27
25
  return (_a = this.data.memberIdsByContainerId.get(containerId)) !== null && _a !== void 0 ? _a : [];
28
26
  }
27
+ memberDocumentElementsOf(containerId) {
28
+ const members = [];
29
+ for (const memberId of this.memberIdsOf(containerId)) {
30
+ const member = this.data.documentElementsById.get(memberId);
31
+ if (member) {
32
+ members.push(member);
33
+ }
34
+ }
35
+ return members;
36
+ }
29
37
  containerPathAtPoint(point, elementType) {
30
38
  const covering = this.containersOfType(elementType).filter((container) => (0, document_container_1.containsPoint)(container.box, point));
31
39
  return (0, document_container_1.topmostPerDepth)(covering).map((container) => container.id);
@@ -35,9 +43,17 @@ class DocumentContainerIndex {
35
43
  }
36
44
  containersOfType(elementType) {
37
45
  var _a;
38
- return ((_a = this.data.containerIdsByElementType.get(elementType)) !== null && _a !== void 0 ? _a : [])
39
- .map((containerId) => this.data.containersById.get(containerId))
40
- .filter(Boolean);
46
+ return this.resolveAll((_a = this.data.containerIdsByElementType.get(elementType)) !== null && _a !== void 0 ? _a : []);
47
+ }
48
+ resolveAll(containerIds) {
49
+ const containers = [];
50
+ for (const containerId of containerIds) {
51
+ const container = this.data.containersById.get(containerId);
52
+ if (container) {
53
+ containers.push(container);
54
+ }
55
+ }
56
+ return containers;
41
57
  }
42
58
  containerPath(documentElementId, elementType) {
43
59
  var _a, _b;
@@ -16,6 +16,7 @@ export interface MeasuredDocumentElement {
16
16
  }
17
17
  export declare function toCoordinateBox(x: number, y: number, width: number, height: number): CoordinateBox;
18
18
  export declare function centerOf(box: CoordinateBox): PositionDefinition;
19
+ export declare function anchorOf(documentElement: DocumentElement, box: CoordinateBox): PositionDefinition;
19
20
  export declare function containsPoint(box: CoordinateBox, point: PositionDefinition): boolean;
20
21
  export declare function encloses(outer: CoordinateBox, inner: CoordinateBox): boolean;
21
22
  export declare function isWiderAndTallerThan(box: CoordinateBox, other: CoordinateBox): boolean;
@@ -2,16 +2,21 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toCoordinateBox = toCoordinateBox;
4
4
  exports.centerOf = centerOf;
5
+ exports.anchorOf = anchorOf;
5
6
  exports.containsPoint = containsPoint;
6
7
  exports.encloses = encloses;
7
8
  exports.isWiderAndTallerThan = isWiderAndTallerThan;
8
9
  exports.topmostPerDepth = topmostPerDepth;
10
+ const dynamic_text_util_1 = require("../util/dynamic-text/dynamic-text-util");
9
11
  function toCoordinateBox(x, y, width, height) {
10
12
  return { x, y, width, height, left: x, right: x + width, top: y, bottom: y + height };
11
13
  }
12
14
  function centerOf(box) {
13
15
  return { x: box.left + box.width / 2, y: box.top + box.height / 2 };
14
16
  }
17
+ function anchorOf(documentElement, box) {
18
+ return dynamic_text_util_1.DynamicTextUtil.isDynamicText(documentElement) ? { x: box.left, y: box.top } : centerOf(box);
19
+ }
15
20
  function containsPoint(box, point) {
16
21
  return point.x >= box.left && point.x <= box.right && point.y >= box.top && point.y <= box.bottom;
17
22
  }
@@ -37,6 +37,7 @@ class DocumentContainerService {
37
37
  containerPathsByDocumentElementId.set(documentElementId, pathsByElementType);
38
38
  }
39
39
  return new document_container_index_1.DocumentContainerIndex({
40
+ documentElementsById: new Map(measuredDocumentElements.map((measured) => [measured.documentElement.id, measured.documentElement])),
40
41
  containersById,
41
42
  containerPathsByDocumentElementId,
42
43
  memberIdsByContainerId,
@@ -124,14 +125,14 @@ class DocumentContainerService {
124
125
  return (0, document_container_1.topmostPerDepth)(containers).map((container) => container.id);
125
126
  }
126
127
  static candidateContainers(measured, containers) {
127
- const center = (0, document_container_1.centerOf)(measured.box);
128
+ const anchor = (0, document_container_1.anchorOf)(measured.documentElement, measured.box);
128
129
  const isShape = document_element_constants_1.SHAPE_ELEMENT_TYPES.includes(measured.documentElement.type);
129
130
  return containers.filter((container) => {
130
131
  var _a;
131
132
  if (container.id === measured.documentElement.id || measured.drawOrder <= container.drawOrder) {
132
133
  return false;
133
134
  }
134
- if (!(0, document_container_1.containsPoint)(container.box, center)) {
135
+ if (!(0, document_container_1.containsPoint)(container.box, anchor)) {
135
136
  return false;
136
137
  }
137
138
  if (isShape && (0, document_container_1.isWiderAndTallerThan)(measured.box, container.box)) {
@@ -207,13 +207,14 @@ class DocumentElementFactory {
207
207
  return ['rectangle', 'circle'].indexOf(element === null || element === void 0 ? void 0 : element.type) !== -1 && ((_a = element === null || element === void 0 ? void 0 : element.elementIds) === null || _a === void 0 ? void 0 : _a.length) > 0;
208
208
  }
209
209
  static getBoundingClientRect(documentElement, startElement, endElement) {
210
+ var _a, _b, _c, _d;
210
211
  const { x, y } = this.getPosition(documentElement, startElement, endElement);
211
212
  const { width, height } = this.getSize(documentElement, startElement, endElement);
212
213
  return {
213
214
  x,
214
215
  y,
215
- width,
216
- height,
216
+ width: width * ((_b = (_a = documentElement.scale) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : 1),
217
+ height: height * ((_d = (_c = documentElement.scale) === null || _c === void 0 ? void 0 : _c.y) !== null && _d !== void 0 ? _d : 1),
217
218
  };
218
219
  }
219
220
  static getPosition(documentElement, startElement, endElement) {
@@ -48,8 +48,9 @@ export declare const HARD_CODED_TEXT: {
48
48
  'section.itemOptionCount': string;
49
49
  'section.colorCount': string;
50
50
  };
51
- export declare function isContainerBinding(bindingProperty: string | null | undefined): boolean;
52
51
  export declare class DynamicTextUtil {
52
+ static isDynamicText(documentElement: DocumentElement): boolean;
53
+ static takesValueFromContainer(bindingProperty: string | null | undefined): boolean;
53
54
  static getDynamicTextValue(dynamicTextElement: DocumentElement, property: TypeProperty, propertyLevel: PropertyLevel, container: DynamicTextContainer | null, componentModels: any[], ownerReferenceModel?: any): string;
54
55
  static calculateSize(dynamicTextElement: DocumentElement, entityType: any, text: string): import("../..").SizeDefinition;
55
56
  static getTextFromFrameMemberValues(displayFunction: string, property: any, memberValues: any[]): string;
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DynamicTextUtil = exports.HARD_CODED_TEXT = exports.CLICK_TO_CONFIGURE = exports.MOVE_TO_SECTION_TEXT = exports.MOVE_TO_FRAME_TEXT = exports.DynamicTextDisplayFunction = exports.DYNAMIC_TEXT_DATA_SOURCE = void 0;
4
- exports.isContainerBinding = isContainerBinding;
5
4
  const types_1 = require("@contrail/types");
6
5
  const util_1 = require("@contrail/util");
7
6
  const document_util_1 = require("@contrail/document-util");
@@ -51,14 +50,19 @@ exports.HARD_CODED_TEXT = {
51
50
  };
52
51
  const CONTAINER_PROPERTY_SLUGS = ['name', 'count', 'itemFamilyCount', 'itemOptionCount', 'colorCount'];
53
52
  const CONTAINER_SOURCES = [DYNAMIC_TEXT_DATA_SOURCE.FRAME, DYNAMIC_TEXT_DATA_SOURCE.SECTION];
54
- function isContainerBinding(bindingProperty) {
55
- if (!bindingProperty) {
56
- return false;
57
- }
58
- const [entityType, propertySlug] = bindingProperty.split('.');
59
- return CONTAINER_SOURCES.includes(entityType) && CONTAINER_PROPERTY_SLUGS.includes(propertySlug);
60
- }
61
53
  class DynamicTextUtil {
54
+ static isDynamicText(documentElement) {
55
+ return ((documentElement === null || documentElement === void 0 ? void 0 : documentElement.type) === 'text' &&
56
+ !!documentElement.propertyBindings &&
57
+ !!documentElement.propertyBindingsMetaData);
58
+ }
59
+ static takesValueFromContainer(bindingProperty) {
60
+ if (!bindingProperty) {
61
+ return false;
62
+ }
63
+ const [entityType, propertySlug] = bindingProperty.split('.');
64
+ return CONTAINER_SOURCES.includes(entityType) && CONTAINER_PROPERTY_SLUGS.includes(propertySlug);
65
+ }
62
66
  static getDynamicTextValue(dynamicTextElement, property, propertyLevel, container, componentModels, ownerReferenceModel = null) {
63
67
  var _a, _b, _c, _d;
64
68
  if (!dynamicTextElement.propertyBindings || !dynamicTextElement.propertyBindings.text) {
@@ -150,12 +154,12 @@ class DynamicTextUtil {
150
154
  return text;
151
155
  }
152
156
  static calculateSize(dynamicTextElement, entityType, text) {
153
- var _a, _b, _c, _d;
154
- let size = dynamicTextElement.size;
155
- const fontFamily = (_a = document_util_1.DocumentStyleUtil.getFirstFontFamily(dynamicTextElement === null || dynamicTextElement === void 0 ? void 0 : dynamicTextElement.text)) !== null && _a !== void 0 ? _a : document_util_1.DEFAULT_TEXT_FONT_FAMILY;
156
- const fontSize = (_d = (dynamicTextElement.isTextTool
157
- ? (_c = (_b = dynamicTextElement.style) === null || _b === void 0 ? void 0 : _b.font) === null || _c === void 0 ? void 0 : _c.size
158
- : document_util_1.DocumentStyleUtil.getFirstFontSize(dynamicTextElement === null || dynamicTextElement === void 0 ? void 0 : dynamicTextElement.text, true))) !== null && _d !== void 0 ? _d : document_util_1.DEFAULT_TEXT_FONT_SIZE;
157
+ var _a, _b, _c, _d, _e;
158
+ let size = (_a = dynamicTextElement.size) !== null && _a !== void 0 ? _a : { width: 0, height: 0 };
159
+ const fontFamily = (_b = document_util_1.DocumentStyleUtil.getFirstFontFamily(dynamicTextElement === null || dynamicTextElement === void 0 ? void 0 : dynamicTextElement.text)) !== null && _b !== void 0 ? _b : document_util_1.DEFAULT_TEXT_FONT_FAMILY;
160
+ const fontSize = (_e = (dynamicTextElement.isTextTool
161
+ ? (_d = (_c = dynamicTextElement.style) === null || _c === void 0 ? void 0 : _c.font) === null || _d === void 0 ? void 0 : _d.size
162
+ : document_util_1.DocumentStyleUtil.getFirstFontSize(dynamicTextElement === null || dynamicTextElement === void 0 ? void 0 : dynamicTextElement.text, true))) !== null && _e !== void 0 ? _e : document_util_1.DEFAULT_TEXT_FONT_SIZE;
159
163
  const fontSizePx = document_util_1.DocumentStyleUtil.toPx(fontSize, 4);
160
164
  const padding = dynamicTextElement.isTextTool ? document_util_1.TEXT_TOOL_PADDING : document_util_1.TEXT_BOX_PADDING;
161
165
  const bold = document_util_1.DocumentStyleUtil.isBold(dynamicTextElement === null || dynamicTextElement === void 0 ? void 0 : dynamicTextElement.text, fontFamily);
@@ -169,7 +173,7 @@ class DynamicTextUtil {
169
173
  italic,
170
174
  };
171
175
  if (!dynamicTextElement.isTextTool) {
172
- measureTextSettings.width = dynamicTextElement.size.width - padding * 2;
176
+ measureTextSettings.width = size.width - padding * 2;
173
177
  }
174
178
  try {
175
179
  const dimensions = document_util_1.DocumentTextUtil.measureText(text, measureTextSettings);
@@ -212,13 +216,14 @@ class DynamicTextUtil {
212
216
  else if (displayFunction === DynamicTextDisplayFunction.LAST) {
213
217
  text = formatter.formatValueForProperty(memberValues[memberValues.length - 1], property);
214
218
  }
215
- else if (displayFunction === DynamicTextDisplayFunction.MIN) {
216
- memberValues = memberValues.filter((e) => e !== null && e !== undefined);
217
- text = formatter.formatValueForProperty(Math.min(...memberValues), property);
218
- }
219
- else if (displayFunction === DynamicTextDisplayFunction.MAX) {
220
- memberValues = memberValues.filter((e) => e !== null && e !== undefined);
221
- text = formatter.formatValueForProperty(Math.max(...memberValues), property);
219
+ else if (displayFunction === DynamicTextDisplayFunction.MIN ||
220
+ displayFunction === DynamicTextDisplayFunction.MAX) {
221
+ const present = memberValues.filter((e) => e !== null && e !== undefined);
222
+ if (!present.length) {
223
+ return '';
224
+ }
225
+ const extreme = displayFunction === DynamicTextDisplayFunction.MIN ? Math.min(...present) : Math.max(...present);
226
+ text = formatter.formatValueForProperty(extreme, property);
222
227
  }
223
228
  return text;
224
229
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/documents",
3
- "version": "2.1.0-alpha.4325cd8",
3
+ "version": "2.1.0-alpha.5e3a42c",
4
4
  "description": "Documents library for contrail platform",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",