@contrail/documents 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ All notable changes to `@contrail/documents` are documented here.
5
5
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6
6
  Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.2.0] - 2026-09-15
9
+
10
+ ### Added
11
+
12
+ - `ItemComponentService.isAnnotationRowEmpty(annotationElement, annotations)` — whether an item card's annotation row will render nothing, because the row is hidden or the item has no `category === 'property'` annotations. The single predicate behind the reservation, so the layout service and the canvas renderers cannot drift apart on the question. A missing row element is not treated as hidden: renderers supply a default row for item components that have none stored, so only the annotation data decides there.
13
+ - `ItemComponentService.getAnnotationRowHeight(annotationElement, annotations, width)` — the row's reserved height, wrapping onto further rows when the badges do not fit `width`, and `0` when the row is empty. Every creation path must size the row through here so a card laid out by the Item Chooser and one laid out by board generation agree.
14
+
15
+ ### Fixed
16
+
17
+ - `updateSizeAndPosition` no longer reserves space above the thumbnail for an annotation row that renders nothing. `calculateAnnotationHeight` returned a flat `fontSize + ANNOTATION_PADDING_Y` minimum even when the row was hidden or the item had no annotations, pushing the thumbnail down ~8px on every such card (DESIGN-150). It now returns `0` in those cases and the thumbnail sits at the card origin. Cards whose row does show a badge are unchanged.
18
+
8
19
  ## [1.19.0] - 2026-09-11
9
20
 
10
21
  ### Added
@@ -15,6 +15,8 @@ export interface ScaledItemCardResult {
15
15
  export declare class ItemComponentService {
16
16
  static isItemComponent(element: DocumentElement): boolean;
17
17
  static isThumbnailOnlyComponent(element: DocumentElement): boolean;
18
+ static isAnnotationRowEmpty(annotationElement: DocumentElement, annotations?: any[]): boolean;
19
+ static getAnnotationRowHeight(annotationElement: DocumentElement, annotations?: any[], width?: number): number;
18
20
  static updateSizeAndPosition(propertyElements: any[], componentElement: DocumentElement, annotatedElements?: any[]): any;
19
21
  private static getYMargin;
20
22
  private static calculateAnnotationHeight;
@@ -17,6 +17,30 @@ class ItemComponentService {
17
17
  const imageElement = element.elements.find((e) => e.type == 'image');
18
18
  return (!annotationElement || annotationElement.isHidden) && imageElement && !imageElement.isHidden;
19
19
  }
20
+ static isAnnotationRowEmpty(annotationElement, annotations = []) {
21
+ if (annotationElement === null || annotationElement === void 0 ? void 0 : annotationElement.isHidden) {
22
+ return true;
23
+ }
24
+ return !(annotations === null || annotations === void 0 ? void 0 : annotations.some((annotation) => (annotation === null || annotation === void 0 ? void 0 : annotation.category) === 'property'));
25
+ }
26
+ static getAnnotationRowHeight(annotationElement, annotations = [], width = exports.DEFAULT_THUMBNAIL_WIDTH) {
27
+ var _a, _b;
28
+ if (this.isAnnotationRowEmpty(annotationElement, annotations)) {
29
+ return 0;
30
+ }
31
+ const annotationSize = ((_b = (_a = annotationElement === null || annotationElement === void 0 ? void 0 : annotationElement.style) === null || _a === void 0 ? void 0 : _a.font) === null || _b === void 0 ? void 0 : _b.size) || document_element_constants_1.ANNOTATION_IMG_SIZE;
32
+ const propertyAnnotations = annotations.filter((annotation) => (annotation === null || annotation === void 0 ? void 0 : annotation.category) === 'property');
33
+ let height = annotationSize + document_element_constants_1.ANNOTATION_PADDING_Y;
34
+ let count = 0;
35
+ propertyAnnotations.forEach(() => {
36
+ if ((count + 1) * annotationSize * 1.25 > width) {
37
+ count = 0;
38
+ height = height + annotationSize + document_element_constants_1.ANNOTATION_PADDING_Y;
39
+ }
40
+ count++;
41
+ });
42
+ return height;
43
+ }
20
44
  static updateSizeAndPosition(propertyElements, componentElement, annotatedElements = []) {
21
45
  var _a, _b, _c, _d;
22
46
  const newElements = [];
@@ -64,9 +88,11 @@ class ItemComponentService {
64
88
  lastYPosition = element.position.y;
65
89
  }
66
90
  const annotationHeight = this.calculateAnnotationHeight(element, componentElement, width, annotatedElements);
67
- lastYPosition = lastYPosition + annotationHeight;
68
91
  if (annotationHeight === 0 && index === 0) {
69
- lastYPosition = lastYPosition + document_element_constants_1.ANNOTATION_IMG_SIZE;
92
+ lastYPosition = 0;
93
+ }
94
+ else {
95
+ lastYPosition = lastYPosition + annotationHeight;
70
96
  }
71
97
  }
72
98
  element.size.width = width;
@@ -112,28 +138,8 @@ class ItemComponentService {
112
138
  return marginY;
113
139
  }
114
140
  static calculateAnnotationHeight(element, componentElement, width, annotatedElements = []) {
115
- var _a, _b;
116
- const annotationSize = ((_b = (_a = element.style) === null || _a === void 0 ? void 0 : _a.font) === null || _b === void 0 ? void 0 : _b.size) || document_element_constants_1.ANNOTATION_IMG_SIZE;
117
- let height = annotationSize + document_element_constants_1.ANNOTATION_PADDING_Y;
118
- let xPos = 0;
119
141
  const annotatedElement = annotatedElements.find((elem) => elem.id === componentElement.id);
120
- if (!annotatedElement || element.isHidden) {
121
- return height;
122
- }
123
- const annotations = annotatedElement.annotations.filter((annotation) => annotation.category === 'property');
124
- if (annotations.length > 0) {
125
- let count = 0;
126
- annotations === null || annotations === void 0 ? void 0 : annotations.forEach((annotation) => {
127
- if ((count + 1) * annotationSize * 1.25 > width) {
128
- count = 0;
129
- xPos = -width * 0.5;
130
- height = height + annotationSize + document_element_constants_1.ANNOTATION_PADDING_Y;
131
- }
132
- xPos = xPos + annotationSize * 1.25;
133
- count++;
134
- });
135
- }
136
- return height;
142
+ return this.getAnnotationRowHeight(element, annotatedElement === null || annotatedElement === void 0 ? void 0 : annotatedElement.annotations, width);
137
143
  }
138
144
  static getImageElement(element) {
139
145
  var _a;
@@ -1,5 +1,8 @@
1
1
  export declare const ANNOTATION_IMG_SIZE = 15;
2
2
  export declare const ANNOTATION_PADDING_Y = 8;
3
+ export declare const COMPONENT_PADDING_X = 8;
4
+ export declare const COMPONENT_PADDING_T = 18;
5
+ export declare const COMPONENT_PADDING_B = 8;
3
6
  export declare const COLOR_PADDING = 5;
4
7
  export declare const COLOR_FONT_SIZE = 8;
5
8
  export declare const COLOR_WIDTH = 140;
@@ -1,8 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.COLOR_WIDTH = exports.COLOR_FONT_SIZE = exports.COLOR_PADDING = exports.ANNOTATION_PADDING_Y = exports.ANNOTATION_IMG_SIZE = void 0;
3
+ exports.COLOR_WIDTH = exports.COLOR_FONT_SIZE = exports.COLOR_PADDING = exports.COMPONENT_PADDING_B = exports.COMPONENT_PADDING_T = exports.COMPONENT_PADDING_X = exports.ANNOTATION_PADDING_Y = exports.ANNOTATION_IMG_SIZE = void 0;
4
4
  exports.ANNOTATION_IMG_SIZE = 15;
5
5
  exports.ANNOTATION_PADDING_Y = 8;
6
+ exports.COMPONENT_PADDING_X = 8;
7
+ exports.COMPONENT_PADDING_T = 18;
8
+ exports.COMPONENT_PADDING_B = 8;
6
9
  exports.COLOR_PADDING = 5;
7
10
  exports.COLOR_FONT_SIZE = 8;
8
11
  exports.COLOR_WIDTH = 140;
@@ -16,7 +16,7 @@ export declare class DocumentElementFactory {
16
16
  newElements: DocumentElement[];
17
17
  };
18
18
  static isMask(element: DocumentElement): boolean;
19
- static getBoundingClientRect(documentElement: DocumentElement, startElement?: DocumentElement, endElement?: DocumentElement): {
19
+ static getBoundingClientRect(documentElement: DocumentElement, startElement?: DocumentElement, endElement?: DocumentElement, annotations?: any[]): {
20
20
  x: any;
21
21
  y: any;
22
22
  width: any;
@@ -25,5 +25,6 @@ export declare class DocumentElementFactory {
25
25
  private static getPosition;
26
26
  private static getSize;
27
27
  private static getComponentSize;
28
+ private static getComponentPaddingTop;
28
29
  static getLineDefinition(documentElement: DocumentElement, startElement?: DocumentElement, endElement?: DocumentElement): LineDefinition;
29
30
  }
@@ -8,6 +8,7 @@ const components_1 = require("./components/components");
8
8
  const document_element_property_binding_handler_1 = require("./document-element-property-binding-handler");
9
9
  const document_table_1 = require("@contrail/document-table");
10
10
  const document_element_constants_1 = require("./document-element-constants");
11
+ const item_component_service_1 = require("./components/item-component.service");
11
12
  const callout_1 = require("./util/callout/callout");
12
13
  class DocumentElementFactory {
13
14
  static initNewElement(type) {
@@ -206,9 +207,9 @@ class DocumentElementFactory {
206
207
  var _a;
207
208
  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
209
  }
209
- static getBoundingClientRect(documentElement, startElement, endElement) {
210
+ static getBoundingClientRect(documentElement, startElement, endElement, annotations) {
210
211
  const { x, y } = this.getPosition(documentElement, startElement, endElement);
211
- const { width, height } = this.getSize(documentElement, startElement, endElement);
212
+ const { width, height } = this.getSize(documentElement, startElement, endElement, annotations);
212
213
  return {
213
214
  x,
214
215
  y,
@@ -235,7 +236,7 @@ class DocumentElementFactory {
235
236
  (documentElement.type === 'component' && ((_b = documentElement === null || documentElement === void 0 ? void 0 : documentElement.modelBindings) === null || _b === void 0 ? void 0 : _b.item) ? 18 : 0),
236
237
  };
237
238
  }
238
- static getSize(documentElement, startElement, endElement) {
239
+ static getSize(documentElement, startElement, endElement, annotations) {
239
240
  if (documentElement.type === 'line' || documentElement.type === 'callout') {
240
241
  const lineDefinition = this.getLineDefinition(documentElement, startElement, endElement);
241
242
  if (!lineDefinition) {
@@ -246,10 +247,12 @@ class DocumentElementFactory {
246
247
  height: Math.abs(lineDefinition.y1 - lineDefinition.y2),
247
248
  };
248
249
  }
249
- return documentElement.type === 'component' ? this.getComponentSize(documentElement) : documentElement.size;
250
+ return documentElement.type === 'component'
251
+ ? this.getComponentSize(documentElement, annotations)
252
+ : documentElement.size;
250
253
  }
251
- static getComponentSize(documentElement) {
252
- var _a, _b, _c, _d, _e, _f;
254
+ static getComponentSize(documentElement, annotations) {
255
+ var _a, _b, _c, _d, _e;
253
256
  let width = 0, height = 0, lastY = null;
254
257
  for (let i = 0; i < ((_a = documentElement === null || documentElement === void 0 ? void 0 : documentElement.elements) === null || _a === void 0 ? void 0 : _a.length); i++) {
255
258
  const element = documentElement.elements[i];
@@ -263,11 +266,26 @@ class DocumentElementFactory {
263
266
  lastY = y + renderedHeight;
264
267
  }
265
268
  }
269
+ if (!((_e = documentElement === null || documentElement === void 0 ? void 0 : documentElement.modelBindings) === null || _e === void 0 ? void 0 : _e.item)) {
270
+ return { width, height };
271
+ }
266
272
  return {
267
- width: width + (((_e = documentElement === null || documentElement === void 0 ? void 0 : documentElement.modelBindings) === null || _e === void 0 ? void 0 : _e.item) ? 8 + 8 : 0),
268
- height: height + (((_f = documentElement === null || documentElement === void 0 ? void 0 : documentElement.modelBindings) === null || _f === void 0 ? void 0 : _f.item) ? 18 + 8 : 0),
273
+ width: width + document_element_constants_1.COMPONENT_PADDING_X * 2,
274
+ height: height + this.getComponentPaddingTop(documentElement, annotations) + document_element_constants_1.COMPONENT_PADDING_B,
269
275
  };
270
276
  }
277
+ static getComponentPaddingTop(documentElement, annotations) {
278
+ var _a, _b, _c, _d, _e;
279
+ if (!annotations) {
280
+ return document_element_constants_1.COMPONENT_PADDING_T;
281
+ }
282
+ const annotationElement = (_a = documentElement === null || documentElement === void 0 ? void 0 : documentElement.elements) === null || _a === void 0 ? void 0 : _a.find((element) => element.type === 'annotation');
283
+ if (!item_component_service_1.ItemComponentService.isAnnotationRowEmpty(annotationElement, annotations)) {
284
+ return document_element_constants_1.COMPONENT_PADDING_T;
285
+ }
286
+ const contentOffsetY = (_e = (_d = (_c = (_b = documentElement === null || documentElement === void 0 ? void 0 : documentElement.elements) === null || _b === void 0 ? void 0 : _b.find((element) => element.type === 'image')) === null || _c === void 0 ? void 0 : _c.position) === null || _d === void 0 ? void 0 : _d.y) !== null && _e !== void 0 ? _e : 0;
287
+ return -contentOffsetY;
288
+ }
271
289
  static getLineDefinition(documentElement, startElement, endElement) {
272
290
  var _a, _b;
273
291
  if (documentElement.type === 'line')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/documents",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "Documents library for contrail platform",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",