ugcinc-render 1.5.23 → 1.5.25

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/index.d.mts CHANGED
@@ -1239,9 +1239,10 @@ declare function getReferenceElementY(elements: ImageEditorElement[], elementId:
1239
1239
  * @param dynamicCrop - Crop configuration
1240
1240
  * @param canvasWidth - Original canvas width
1241
1241
  * @param canvasHeight - Original canvas height
1242
+ * @param textValues - Optional map of textInputId -> text content for height calculation
1242
1243
  * @returns CropBounds with x, y, width, height
1243
1244
  */
1244
- declare function calculateCropBounds(elements: ImageEditorElement[], dynamicCrop: DynamicCropConfig | undefined, canvasWidth: number, canvasHeight: number): CropBounds;
1245
+ declare function calculateCropBounds(elements: ImageEditorElement[], dynamicCrop: DynamicCropConfig | undefined, canvasWidth: number, canvasHeight: number, textValues?: Record<string, string>): CropBounds;
1245
1246
  /**
1246
1247
  * Check if dynamic crop is enabled (either vertical or horizontal)
1247
1248
  */
package/dist/index.d.ts CHANGED
@@ -1239,9 +1239,10 @@ declare function getReferenceElementY(elements: ImageEditorElement[], elementId:
1239
1239
  * @param dynamicCrop - Crop configuration
1240
1240
  * @param canvasWidth - Original canvas width
1241
1241
  * @param canvasHeight - Original canvas height
1242
+ * @param textValues - Optional map of textInputId -> text content for height calculation
1242
1243
  * @returns CropBounds with x, y, width, height
1243
1244
  */
1244
- declare function calculateCropBounds(elements: ImageEditorElement[], dynamicCrop: DynamicCropConfig | undefined, canvasWidth: number, canvasHeight: number): CropBounds;
1245
+ declare function calculateCropBounds(elements: ImageEditorElement[], dynamicCrop: DynamicCropConfig | undefined, canvasWidth: number, canvasHeight: number, textValues?: Record<string, string>): CropBounds;
1245
1246
  /**
1246
1247
  * Check if dynamic crop is enabled (either vertical or horizontal)
1247
1248
  */
package/dist/index.js CHANGED
@@ -944,38 +944,40 @@ function topologicalSortForAxis(elements, axis) {
944
944
  }
945
945
  return { sorted, errors };
946
946
  }
947
- function getSelfAnchorOffsetX(element, selfAnchor) {
947
+ function getSelfAnchorOffsetX(element, selfAnchor, actualWidth) {
948
+ const width = actualWidth ?? element.width;
948
949
  switch (selfAnchor) {
949
950
  case "left":
950
951
  return 0;
951
952
  case "center":
952
- return element.width / 2;
953
+ return width / 2;
953
954
  case "right":
954
- return element.width;
955
+ return width;
955
956
  default:
956
957
  return 0;
957
958
  }
958
959
  }
959
- function getSelfAnchorOffsetY(element, selfAnchor) {
960
+ function getSelfAnchorOffsetY(element, selfAnchor, actualHeight) {
961
+ const height = actualHeight ?? element.height;
960
962
  switch (selfAnchor) {
961
963
  case "top":
962
964
  return 0;
963
965
  case "middle":
964
- return element.height / 2;
966
+ return height / 2;
965
967
  case "bottom":
966
- return element.height;
968
+ return height;
967
969
  default:
968
970
  return 0;
969
971
  }
970
972
  }
971
- function calculateAbsoluteX(element, referenceElement, anchor, selfAnchor, offset) {
973
+ function calculateAbsoluteX(element, referenceElement, anchor, selfAnchor, offset, actualWidth) {
972
974
  const refX = anchor === "left" ? referenceElement.x : referenceElement.x + referenceElement.width;
973
- const selfOffset = getSelfAnchorOffsetX(element, selfAnchor);
975
+ const selfOffset = getSelfAnchorOffsetX(element, selfAnchor, actualWidth);
974
976
  return refX + offset - selfOffset;
975
977
  }
976
- function calculateAbsoluteY(element, referenceElement, anchor, selfAnchor, offset) {
978
+ function calculateAbsoluteY(element, referenceElement, anchor, selfAnchor, offset, actualHeight) {
977
979
  const refY = anchor === "top" ? referenceElement.y : referenceElement.y + referenceElement.height;
978
- const selfOffset = getSelfAnchorOffsetY(element, selfAnchor);
980
+ const selfOffset = getSelfAnchorOffsetY(element, selfAnchor, actualHeight);
979
981
  return refY + offset - selfOffset;
980
982
  }
981
983
  function resolveElementPositions(elements, textValues) {
@@ -1033,26 +1035,37 @@ function resolveElementPositions(elements, textValues) {
1033
1035
  if (refPosition) {
1034
1036
  const defaultSelfAnchor = elem.relativePositionX.anchor === "right" ? "left" : "right";
1035
1037
  const selfAnchor = elem.relativePositionX.selfAnchor ?? defaultSelfAnchor;
1038
+ let actualWidth;
1039
+ let autoResult = null;
1040
+ if (elem.type === "text") {
1041
+ const textContent = getTextContent(elem);
1042
+ autoResult = calculateAutoWidthDimensions(elem, textContent, measureCtx);
1043
+ if (autoResult && elem.autoWidth) {
1044
+ actualWidth = autoResult.actualWidth;
1045
+ }
1046
+ }
1036
1047
  const newX = calculateAbsoluteX(
1037
1048
  elem,
1038
1049
  refPosition,
1039
1050
  elem.relativePositionX.anchor,
1040
1051
  selfAnchor,
1041
- elem.relativePositionX.offset
1052
+ elem.relativePositionX.offset,
1053
+ actualWidth
1054
+ // Use actual width for self anchor calculation
1042
1055
  );
1043
1056
  resolvedX.set(elem.id, { x: newX, width: elem.width });
1044
- if (elem.type === "text") {
1045
- const textContent = getTextContent(elem);
1046
- const autoResult = calculateAutoWidthDimensions({ ...elem, x: newX }, textContent, measureCtx);
1047
- if (autoResult) {
1048
- if (elem.autoWidth) {
1049
- referenceX.set(elem.id, { x: autoResult.actualX, width: autoResult.actualWidth });
1057
+ if (autoResult) {
1058
+ if (elem.autoWidth) {
1059
+ const updatedResult = calculateAutoWidthDimensions({ ...elem, x: newX }, getTextContent(elem), measureCtx);
1060
+ if (updatedResult) {
1061
+ referenceX.set(elem.id, { x: updatedResult.actualX, width: updatedResult.actualWidth });
1062
+ referenceY.set(elem.id, { y: referenceY.get(elem.id)?.y ?? elem.y, height: updatedResult.actualHeight });
1050
1063
  } else {
1051
1064
  referenceX.set(elem.id, { x: newX, width: elem.width });
1052
1065
  }
1053
- referenceY.set(elem.id, { y: referenceY.get(elem.id)?.y ?? elem.y, height: autoResult.actualHeight });
1054
1066
  } else {
1055
1067
  referenceX.set(elem.id, { x: newX, width: elem.width });
1068
+ referenceY.set(elem.id, { y: referenceY.get(elem.id)?.y ?? elem.y, height: autoResult.actualHeight });
1056
1069
  }
1057
1070
  } else {
1058
1071
  referenceX.set(elem.id, { x: newX, width: elem.width });
@@ -1067,22 +1080,26 @@ function resolveElementPositions(elements, textValues) {
1067
1080
  if (refPosition) {
1068
1081
  const defaultSelfAnchor = elem.relativePositionY.anchor === "bottom" ? "top" : "bottom";
1069
1082
  const selfAnchor = elem.relativePositionY.selfAnchor ?? defaultSelfAnchor;
1083
+ let actualHeight;
1084
+ if (elem.type === "text") {
1085
+ const textContent = getTextContent(elem);
1086
+ const autoResult = calculateAutoWidthDimensions(elem, textContent, measureCtx);
1087
+ if (autoResult) {
1088
+ actualHeight = autoResult.actualHeight;
1089
+ }
1090
+ }
1070
1091
  const newY = calculateAbsoluteY(
1071
1092
  elem,
1072
1093
  refPosition,
1073
1094
  elem.relativePositionY.anchor,
1074
1095
  selfAnchor,
1075
- elem.relativePositionY.offset
1096
+ elem.relativePositionY.offset,
1097
+ actualHeight
1098
+ // Use actual height for self anchor calculation
1076
1099
  );
1077
1100
  resolvedY.set(elem.id, { y: newY, height: elem.height });
1078
- if (elem.type === "text") {
1079
- const textContent = getTextContent(elem);
1080
- const autoResult = calculateAutoWidthDimensions({ ...elem, y: newY }, textContent, measureCtx);
1081
- if (autoResult) {
1082
- referenceY.set(elem.id, { y: newY, height: autoResult.actualHeight });
1083
- } else {
1084
- referenceY.set(elem.id, { y: newY, height: elem.height });
1085
- }
1101
+ if (actualHeight !== void 0) {
1102
+ referenceY.set(elem.id, { y: newY, height: actualHeight });
1086
1103
  } else {
1087
1104
  referenceY.set(elem.id, { y: newY, height: elem.height });
1088
1105
  }
@@ -1150,7 +1167,16 @@ function getReferenceElementY(elements, elementId) {
1150
1167
  }
1151
1168
 
1152
1169
  // src/utils/cropBounds.ts
1153
- function calculateCropBounds(elements, dynamicCrop, canvasWidth, canvasHeight) {
1170
+ function getActualElementHeight(elem, textValues, measureCtx) {
1171
+ if (elem.type !== "text") return elem.height;
1172
+ let textContent = elem.text ?? "Text";
1173
+ if (elem.textInputId && textValues?.[elem.textInputId]) {
1174
+ textContent = textValues[elem.textInputId];
1175
+ }
1176
+ const result = calculateAutoWidthDimensions(elem, textContent, measureCtx);
1177
+ return result?.actualHeight ?? elem.height;
1178
+ }
1179
+ function calculateCropBounds(elements, dynamicCrop, canvasWidth, canvasHeight, textValues) {
1154
1180
  if (!dynamicCrop) {
1155
1181
  return { x: 0, y: 0, width: canvasWidth, height: canvasHeight };
1156
1182
  }
@@ -1163,6 +1189,12 @@ function calculateCropBounds(elements, dynamicCrop, canvasWidth, canvasHeight) {
1163
1189
  if (boundary.elementId) return boundary.elementId;
1164
1190
  return void 0;
1165
1191
  };
1192
+ let measureCtx = null;
1193
+ const hasTextElements = elements.some((e) => e.type === "text");
1194
+ if (hasTextElements && typeof document !== "undefined") {
1195
+ const canvas = document.createElement("canvas");
1196
+ measureCtx = canvas.getContext("2d");
1197
+ }
1166
1198
  let cropY = 0;
1167
1199
  let cropHeight = canvasHeight;
1168
1200
  if (dynamicCrop.vertical?.enabled) {
@@ -1173,8 +1205,9 @@ function calculateCropBounds(elements, dynamicCrop, canvasWidth, canvasHeight) {
1173
1205
  let minY = canvasHeight;
1174
1206
  let maxY = 0;
1175
1207
  for (const elem of elements) {
1208
+ const actualHeight = getActualElementHeight(elem, textValues, measureCtx);
1176
1209
  minY = Math.min(minY, elem.y);
1177
- maxY = Math.max(maxY, elem.y + elem.height);
1210
+ maxY = Math.max(maxY, elem.y + actualHeight);
1178
1211
  }
1179
1212
  if (elements.length > 0) {
1180
1213
  cropY = Math.max(0, minY - paddingStart);
@@ -1195,7 +1228,8 @@ function calculateCropBounds(elements, dynamicCrop, canvasWidth, canvasHeight) {
1195
1228
  if (endElementId) {
1196
1229
  const endElem = elementMap.get(endElementId);
1197
1230
  if (endElem) {
1198
- bottomY = endElem.y + endElem.height;
1231
+ const actualHeight = getActualElementHeight(endElem, textValues, measureCtx);
1232
+ bottomY = endElem.y + actualHeight;
1199
1233
  }
1200
1234
  }
1201
1235
  cropY = Math.max(0, topY - paddingStart);
@@ -1429,8 +1463,8 @@ function ImageEditorComposition({
1429
1463
  }, [elements, textValues]);
1430
1464
  const cropBounds = (0, import_react3.useMemo)(() => {
1431
1465
  if (!isDynamicCropEnabled(dynamicCrop) || !resolvedElements) return null;
1432
- return calculateCropBounds(resolvedElements, dynamicCrop, canvasWidth, canvasHeight);
1433
- }, [resolvedElements, dynamicCrop, canvasWidth, canvasHeight]);
1466
+ return calculateCropBounds(resolvedElements, dynamicCrop, canvasWidth, canvasHeight, textValues);
1467
+ }, [resolvedElements, dynamicCrop, canvasWidth, canvasHeight, textValues]);
1434
1468
  const segmentsFromElements = (0, import_react3.useMemo)(() => {
1435
1469
  if (!resolvedElements) return null;
1436
1470
  const segments = [];
package/dist/index.mjs CHANGED
@@ -858,38 +858,40 @@ function topologicalSortForAxis(elements, axis) {
858
858
  }
859
859
  return { sorted, errors };
860
860
  }
861
- function getSelfAnchorOffsetX(element, selfAnchor) {
861
+ function getSelfAnchorOffsetX(element, selfAnchor, actualWidth) {
862
+ const width = actualWidth ?? element.width;
862
863
  switch (selfAnchor) {
863
864
  case "left":
864
865
  return 0;
865
866
  case "center":
866
- return element.width / 2;
867
+ return width / 2;
867
868
  case "right":
868
- return element.width;
869
+ return width;
869
870
  default:
870
871
  return 0;
871
872
  }
872
873
  }
873
- function getSelfAnchorOffsetY(element, selfAnchor) {
874
+ function getSelfAnchorOffsetY(element, selfAnchor, actualHeight) {
875
+ const height = actualHeight ?? element.height;
874
876
  switch (selfAnchor) {
875
877
  case "top":
876
878
  return 0;
877
879
  case "middle":
878
- return element.height / 2;
880
+ return height / 2;
879
881
  case "bottom":
880
- return element.height;
882
+ return height;
881
883
  default:
882
884
  return 0;
883
885
  }
884
886
  }
885
- function calculateAbsoluteX(element, referenceElement, anchor, selfAnchor, offset) {
887
+ function calculateAbsoluteX(element, referenceElement, anchor, selfAnchor, offset, actualWidth) {
886
888
  const refX = anchor === "left" ? referenceElement.x : referenceElement.x + referenceElement.width;
887
- const selfOffset = getSelfAnchorOffsetX(element, selfAnchor);
889
+ const selfOffset = getSelfAnchorOffsetX(element, selfAnchor, actualWidth);
888
890
  return refX + offset - selfOffset;
889
891
  }
890
- function calculateAbsoluteY(element, referenceElement, anchor, selfAnchor, offset) {
892
+ function calculateAbsoluteY(element, referenceElement, anchor, selfAnchor, offset, actualHeight) {
891
893
  const refY = anchor === "top" ? referenceElement.y : referenceElement.y + referenceElement.height;
892
- const selfOffset = getSelfAnchorOffsetY(element, selfAnchor);
894
+ const selfOffset = getSelfAnchorOffsetY(element, selfAnchor, actualHeight);
893
895
  return refY + offset - selfOffset;
894
896
  }
895
897
  function resolveElementPositions(elements, textValues) {
@@ -947,26 +949,37 @@ function resolveElementPositions(elements, textValues) {
947
949
  if (refPosition) {
948
950
  const defaultSelfAnchor = elem.relativePositionX.anchor === "right" ? "left" : "right";
949
951
  const selfAnchor = elem.relativePositionX.selfAnchor ?? defaultSelfAnchor;
952
+ let actualWidth;
953
+ let autoResult = null;
954
+ if (elem.type === "text") {
955
+ const textContent = getTextContent(elem);
956
+ autoResult = calculateAutoWidthDimensions(elem, textContent, measureCtx);
957
+ if (autoResult && elem.autoWidth) {
958
+ actualWidth = autoResult.actualWidth;
959
+ }
960
+ }
950
961
  const newX = calculateAbsoluteX(
951
962
  elem,
952
963
  refPosition,
953
964
  elem.relativePositionX.anchor,
954
965
  selfAnchor,
955
- elem.relativePositionX.offset
966
+ elem.relativePositionX.offset,
967
+ actualWidth
968
+ // Use actual width for self anchor calculation
956
969
  );
957
970
  resolvedX.set(elem.id, { x: newX, width: elem.width });
958
- if (elem.type === "text") {
959
- const textContent = getTextContent(elem);
960
- const autoResult = calculateAutoWidthDimensions({ ...elem, x: newX }, textContent, measureCtx);
961
- if (autoResult) {
962
- if (elem.autoWidth) {
963
- referenceX.set(elem.id, { x: autoResult.actualX, width: autoResult.actualWidth });
971
+ if (autoResult) {
972
+ if (elem.autoWidth) {
973
+ const updatedResult = calculateAutoWidthDimensions({ ...elem, x: newX }, getTextContent(elem), measureCtx);
974
+ if (updatedResult) {
975
+ referenceX.set(elem.id, { x: updatedResult.actualX, width: updatedResult.actualWidth });
976
+ referenceY.set(elem.id, { y: referenceY.get(elem.id)?.y ?? elem.y, height: updatedResult.actualHeight });
964
977
  } else {
965
978
  referenceX.set(elem.id, { x: newX, width: elem.width });
966
979
  }
967
- referenceY.set(elem.id, { y: referenceY.get(elem.id)?.y ?? elem.y, height: autoResult.actualHeight });
968
980
  } else {
969
981
  referenceX.set(elem.id, { x: newX, width: elem.width });
982
+ referenceY.set(elem.id, { y: referenceY.get(elem.id)?.y ?? elem.y, height: autoResult.actualHeight });
970
983
  }
971
984
  } else {
972
985
  referenceX.set(elem.id, { x: newX, width: elem.width });
@@ -981,22 +994,26 @@ function resolveElementPositions(elements, textValues) {
981
994
  if (refPosition) {
982
995
  const defaultSelfAnchor = elem.relativePositionY.anchor === "bottom" ? "top" : "bottom";
983
996
  const selfAnchor = elem.relativePositionY.selfAnchor ?? defaultSelfAnchor;
997
+ let actualHeight;
998
+ if (elem.type === "text") {
999
+ const textContent = getTextContent(elem);
1000
+ const autoResult = calculateAutoWidthDimensions(elem, textContent, measureCtx);
1001
+ if (autoResult) {
1002
+ actualHeight = autoResult.actualHeight;
1003
+ }
1004
+ }
984
1005
  const newY = calculateAbsoluteY(
985
1006
  elem,
986
1007
  refPosition,
987
1008
  elem.relativePositionY.anchor,
988
1009
  selfAnchor,
989
- elem.relativePositionY.offset
1010
+ elem.relativePositionY.offset,
1011
+ actualHeight
1012
+ // Use actual height for self anchor calculation
990
1013
  );
991
1014
  resolvedY.set(elem.id, { y: newY, height: elem.height });
992
- if (elem.type === "text") {
993
- const textContent = getTextContent(elem);
994
- const autoResult = calculateAutoWidthDimensions({ ...elem, y: newY }, textContent, measureCtx);
995
- if (autoResult) {
996
- referenceY.set(elem.id, { y: newY, height: autoResult.actualHeight });
997
- } else {
998
- referenceY.set(elem.id, { y: newY, height: elem.height });
999
- }
1015
+ if (actualHeight !== void 0) {
1016
+ referenceY.set(elem.id, { y: newY, height: actualHeight });
1000
1017
  } else {
1001
1018
  referenceY.set(elem.id, { y: newY, height: elem.height });
1002
1019
  }
@@ -1064,7 +1081,16 @@ function getReferenceElementY(elements, elementId) {
1064
1081
  }
1065
1082
 
1066
1083
  // src/utils/cropBounds.ts
1067
- function calculateCropBounds(elements, dynamicCrop, canvasWidth, canvasHeight) {
1084
+ function getActualElementHeight(elem, textValues, measureCtx) {
1085
+ if (elem.type !== "text") return elem.height;
1086
+ let textContent = elem.text ?? "Text";
1087
+ if (elem.textInputId && textValues?.[elem.textInputId]) {
1088
+ textContent = textValues[elem.textInputId];
1089
+ }
1090
+ const result = calculateAutoWidthDimensions(elem, textContent, measureCtx);
1091
+ return result?.actualHeight ?? elem.height;
1092
+ }
1093
+ function calculateCropBounds(elements, dynamicCrop, canvasWidth, canvasHeight, textValues) {
1068
1094
  if (!dynamicCrop) {
1069
1095
  return { x: 0, y: 0, width: canvasWidth, height: canvasHeight };
1070
1096
  }
@@ -1077,6 +1103,12 @@ function calculateCropBounds(elements, dynamicCrop, canvasWidth, canvasHeight) {
1077
1103
  if (boundary.elementId) return boundary.elementId;
1078
1104
  return void 0;
1079
1105
  };
1106
+ let measureCtx = null;
1107
+ const hasTextElements = elements.some((e) => e.type === "text");
1108
+ if (hasTextElements && typeof document !== "undefined") {
1109
+ const canvas = document.createElement("canvas");
1110
+ measureCtx = canvas.getContext("2d");
1111
+ }
1080
1112
  let cropY = 0;
1081
1113
  let cropHeight = canvasHeight;
1082
1114
  if (dynamicCrop.vertical?.enabled) {
@@ -1087,8 +1119,9 @@ function calculateCropBounds(elements, dynamicCrop, canvasWidth, canvasHeight) {
1087
1119
  let minY = canvasHeight;
1088
1120
  let maxY = 0;
1089
1121
  for (const elem of elements) {
1122
+ const actualHeight = getActualElementHeight(elem, textValues, measureCtx);
1090
1123
  minY = Math.min(minY, elem.y);
1091
- maxY = Math.max(maxY, elem.y + elem.height);
1124
+ maxY = Math.max(maxY, elem.y + actualHeight);
1092
1125
  }
1093
1126
  if (elements.length > 0) {
1094
1127
  cropY = Math.max(0, minY - paddingStart);
@@ -1109,7 +1142,8 @@ function calculateCropBounds(elements, dynamicCrop, canvasWidth, canvasHeight) {
1109
1142
  if (endElementId) {
1110
1143
  const endElem = elementMap.get(endElementId);
1111
1144
  if (endElem) {
1112
- bottomY = endElem.y + endElem.height;
1145
+ const actualHeight = getActualElementHeight(endElem, textValues, measureCtx);
1146
+ bottomY = endElem.y + actualHeight;
1113
1147
  }
1114
1148
  }
1115
1149
  cropY = Math.max(0, topY - paddingStart);
@@ -1343,8 +1377,8 @@ function ImageEditorComposition({
1343
1377
  }, [elements, textValues]);
1344
1378
  const cropBounds = useMemo3(() => {
1345
1379
  if (!isDynamicCropEnabled(dynamicCrop) || !resolvedElements) return null;
1346
- return calculateCropBounds(resolvedElements, dynamicCrop, canvasWidth, canvasHeight);
1347
- }, [resolvedElements, dynamicCrop, canvasWidth, canvasHeight]);
1380
+ return calculateCropBounds(resolvedElements, dynamicCrop, canvasWidth, canvasHeight, textValues);
1381
+ }, [resolvedElements, dynamicCrop, canvasWidth, canvasHeight, textValues]);
1348
1382
  const segmentsFromElements = useMemo3(() => {
1349
1383
  if (!resolvedElements) return null;
1350
1384
  const segments = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc-render",
3
- "version": "1.5.23",
3
+ "version": "1.5.25",
4
4
  "description": "Unified rendering package for UGC Inc - shared types, components, and compositions for pixel-perfect client/server rendering",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",