@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 +25 -0
- package/lib/containers/document-container-index.d.ts +7 -4
- package/lib/containers/document-container-index.js +22 -6
- package/lib/containers/document-container.d.ts +1 -0
- package/lib/containers/document-container.js +5 -0
- package/lib/containers/document-container.service.js +3 -2
- package/lib/document-element-factory.js +3 -2
- package/lib/util/dynamic-text/dynamic-text-util.d.ts +2 -1
- package/lib/util/dynamic-text/dynamic-text-util.js +27 -22
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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
|
|
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,
|
|
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 = (
|
|
156
|
-
const fontSize = (
|
|
157
|
-
? (
|
|
158
|
-
: document_util_1.DocumentStyleUtil.getFirstFontSize(dynamicTextElement === null || dynamicTextElement === void 0 ? void 0 : dynamicTextElement.text, true))) !== null &&
|
|
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 =
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
}
|