ugcinc-render 1.5.0 → 1.5.2

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.js CHANGED
@@ -294,6 +294,55 @@ function hexToRgba(hex, opacity = 100) {
294
294
 
295
295
  // src/components/TextElement.tsx
296
296
  var import_jsx_runtime = require("react/jsx-runtime");
297
+ function calculateAutoWidth({
298
+ text,
299
+ maxWidth,
300
+ paddingLeft,
301
+ paddingRight,
302
+ fontSize,
303
+ fontWeight,
304
+ fontFamily,
305
+ letterSpacing
306
+ }) {
307
+ if (typeof document === "undefined") {
308
+ throw new Error("calculateAutoWidth requires a browser environment with document available");
309
+ }
310
+ const canvas = document.createElement("canvas");
311
+ const ctx = canvas.getContext("2d");
312
+ if (!ctx) {
313
+ throw new Error("Failed to get 2D canvas context for text measurement");
314
+ }
315
+ ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
316
+ const availableWidth = maxWidth - paddingLeft - paddingRight;
317
+ const words = text.split(" ");
318
+ const lines = [];
319
+ let currentLine = "";
320
+ for (const word of words) {
321
+ if (!word) continue;
322
+ const testLine = currentLine + (currentLine ? " " : "") + word;
323
+ const charCount = [...testLine].length;
324
+ const extraSpacing = charCount > 1 ? (charCount - 1) * letterSpacing : 0;
325
+ const totalWidth = ctx.measureText(testLine).width + extraSpacing;
326
+ if (totalWidth > availableWidth && currentLine) {
327
+ lines.push(currentLine);
328
+ currentLine = word;
329
+ } else {
330
+ currentLine = testLine;
331
+ }
332
+ }
333
+ if (currentLine) lines.push(currentLine);
334
+ let widestLineWidth = 0;
335
+ for (const line of lines) {
336
+ const chars = [...line];
337
+ let lineWidth = 0;
338
+ for (const char of chars) {
339
+ lineWidth += ctx.measureText(char).width + letterSpacing;
340
+ }
341
+ if (chars.length > 0) lineWidth -= letterSpacing;
342
+ widestLineWidth = Math.max(widestLineWidth, lineWidth);
343
+ }
344
+ return Math.min(widestLineWidth + paddingLeft + paddingRight, maxWidth);
345
+ }
297
346
  function TextElement({ segment, scale = 1 }) {
298
347
  const fontType = segment.fontType ?? TEXT_DEFAULTS.fontType;
299
348
  const fontSize = (segment.fontSize ?? TEXT_DEFAULTS.fontSize) * scale;
@@ -306,10 +355,10 @@ function TextElement({ segment, scale = 1 }) {
306
355
  const strokeColor = segment.strokeColor;
307
356
  const strokeWidth = (segment.strokeWidth ?? TEXT_DEFAULTS.strokeWidth) * scale;
308
357
  const uniformPadding = (segment.padding ?? TEXT_DEFAULTS.padding) * scale;
309
- const paddingTop = segment.paddingTop ?? uniformPadding;
310
- const paddingRight = segment.paddingRight ?? uniformPadding;
311
- const paddingBottom = segment.paddingBottom ?? uniformPadding;
312
- const paddingLeft = segment.paddingLeft ?? uniformPadding;
358
+ const paddingTop = segment.paddingTop !== void 0 ? segment.paddingTop * scale : uniformPadding;
359
+ const paddingRight = segment.paddingRight !== void 0 ? segment.paddingRight * scale : uniformPadding;
360
+ const paddingBottom = segment.paddingBottom !== void 0 ? segment.paddingBottom * scale : uniformPadding;
361
+ const paddingLeft = segment.paddingLeft !== void 0 ? segment.paddingLeft * scale : uniformPadding;
313
362
  const x = segment.xOffset * scale;
314
363
  const y = segment.yOffset * scale;
315
364
  const width = segment.width * scale;
@@ -321,6 +370,19 @@ function TextElement({ segment, scale = 1 }) {
321
370
  const backgroundOpacity = segment.backgroundOpacity ?? TEXT_DEFAULTS.backgroundOpacity;
322
371
  const backgroundBorderRadius = segment.backgroundBorderRadius;
323
372
  const fontFamily = getFontFamily(fontType);
373
+ const calculatedWidth = (0, import_react.useMemo)(() => {
374
+ if (!autoWidth) return width;
375
+ return calculateAutoWidth({
376
+ text: segment.text,
377
+ maxWidth: width,
378
+ paddingLeft,
379
+ paddingRight,
380
+ fontSize,
381
+ fontWeight,
382
+ fontFamily,
383
+ letterSpacing
384
+ });
385
+ }, [autoWidth, segment.text, width, paddingLeft, paddingRight, fontSize, fontWeight, fontFamily, letterSpacing]);
324
386
  const borderRadiusStyle = (0, import_react.useMemo)(() => {
325
387
  if (!backgroundBorderRadius) return void 0;
326
388
  const radii = getBorderRadii(backgroundBorderRadius);
@@ -344,8 +406,10 @@ function TextElement({ segment, scale = 1 }) {
344
406
  }), [x, y, width, height, rotation, verticalAlign, autoWidth, boxAlign]);
345
407
  const backgroundBoxStyle = (0, import_react.useMemo)(() => {
346
408
  const baseStyle = autoWidth ? {
347
- width: "fit-content",
348
- maxWidth: "100%"
409
+ // Use explicit calculated width instead of fit-content
410
+ // This fixes the issue where multi-line text doesn't shrink to widest line
411
+ width: calculatedWidth,
412
+ maxWidth: width
349
413
  } : {
350
414
  // When not autoWidth, let the text div handle everything
351
415
  display: "flex",
@@ -359,7 +423,7 @@ function TextElement({ segment, scale = 1 }) {
359
423
  backgroundColor: hexToRgba(backgroundColor, backgroundOpacity),
360
424
  borderRadius: borderRadiusStyle
361
425
  };
362
- }, [autoWidth, backgroundColor, backgroundOpacity, borderRadiusStyle]);
426
+ }, [autoWidth, calculatedWidth, width, backgroundColor, backgroundOpacity, borderRadiusStyle]);
363
427
  const textStyle = (0, import_react.useMemo)(() => ({
364
428
  fontFamily,
365
429
  fontSize,
package/dist/index.mjs CHANGED
@@ -220,6 +220,55 @@ function hexToRgba(hex, opacity = 100) {
220
220
 
221
221
  // src/components/TextElement.tsx
222
222
  import { jsx } from "react/jsx-runtime";
223
+ function calculateAutoWidth({
224
+ text,
225
+ maxWidth,
226
+ paddingLeft,
227
+ paddingRight,
228
+ fontSize,
229
+ fontWeight,
230
+ fontFamily,
231
+ letterSpacing
232
+ }) {
233
+ if (typeof document === "undefined") {
234
+ throw new Error("calculateAutoWidth requires a browser environment with document available");
235
+ }
236
+ const canvas = document.createElement("canvas");
237
+ const ctx = canvas.getContext("2d");
238
+ if (!ctx) {
239
+ throw new Error("Failed to get 2D canvas context for text measurement");
240
+ }
241
+ ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
242
+ const availableWidth = maxWidth - paddingLeft - paddingRight;
243
+ const words = text.split(" ");
244
+ const lines = [];
245
+ let currentLine = "";
246
+ for (const word of words) {
247
+ if (!word) continue;
248
+ const testLine = currentLine + (currentLine ? " " : "") + word;
249
+ const charCount = [...testLine].length;
250
+ const extraSpacing = charCount > 1 ? (charCount - 1) * letterSpacing : 0;
251
+ const totalWidth = ctx.measureText(testLine).width + extraSpacing;
252
+ if (totalWidth > availableWidth && currentLine) {
253
+ lines.push(currentLine);
254
+ currentLine = word;
255
+ } else {
256
+ currentLine = testLine;
257
+ }
258
+ }
259
+ if (currentLine) lines.push(currentLine);
260
+ let widestLineWidth = 0;
261
+ for (const line of lines) {
262
+ const chars = [...line];
263
+ let lineWidth = 0;
264
+ for (const char of chars) {
265
+ lineWidth += ctx.measureText(char).width + letterSpacing;
266
+ }
267
+ if (chars.length > 0) lineWidth -= letterSpacing;
268
+ widestLineWidth = Math.max(widestLineWidth, lineWidth);
269
+ }
270
+ return Math.min(widestLineWidth + paddingLeft + paddingRight, maxWidth);
271
+ }
223
272
  function TextElement({ segment, scale = 1 }) {
224
273
  const fontType = segment.fontType ?? TEXT_DEFAULTS.fontType;
225
274
  const fontSize = (segment.fontSize ?? TEXT_DEFAULTS.fontSize) * scale;
@@ -232,10 +281,10 @@ function TextElement({ segment, scale = 1 }) {
232
281
  const strokeColor = segment.strokeColor;
233
282
  const strokeWidth = (segment.strokeWidth ?? TEXT_DEFAULTS.strokeWidth) * scale;
234
283
  const uniformPadding = (segment.padding ?? TEXT_DEFAULTS.padding) * scale;
235
- const paddingTop = segment.paddingTop ?? uniformPadding;
236
- const paddingRight = segment.paddingRight ?? uniformPadding;
237
- const paddingBottom = segment.paddingBottom ?? uniformPadding;
238
- const paddingLeft = segment.paddingLeft ?? uniformPadding;
284
+ const paddingTop = segment.paddingTop !== void 0 ? segment.paddingTop * scale : uniformPadding;
285
+ const paddingRight = segment.paddingRight !== void 0 ? segment.paddingRight * scale : uniformPadding;
286
+ const paddingBottom = segment.paddingBottom !== void 0 ? segment.paddingBottom * scale : uniformPadding;
287
+ const paddingLeft = segment.paddingLeft !== void 0 ? segment.paddingLeft * scale : uniformPadding;
239
288
  const x = segment.xOffset * scale;
240
289
  const y = segment.yOffset * scale;
241
290
  const width = segment.width * scale;
@@ -247,6 +296,19 @@ function TextElement({ segment, scale = 1 }) {
247
296
  const backgroundOpacity = segment.backgroundOpacity ?? TEXT_DEFAULTS.backgroundOpacity;
248
297
  const backgroundBorderRadius = segment.backgroundBorderRadius;
249
298
  const fontFamily = getFontFamily(fontType);
299
+ const calculatedWidth = useMemo(() => {
300
+ if (!autoWidth) return width;
301
+ return calculateAutoWidth({
302
+ text: segment.text,
303
+ maxWidth: width,
304
+ paddingLeft,
305
+ paddingRight,
306
+ fontSize,
307
+ fontWeight,
308
+ fontFamily,
309
+ letterSpacing
310
+ });
311
+ }, [autoWidth, segment.text, width, paddingLeft, paddingRight, fontSize, fontWeight, fontFamily, letterSpacing]);
250
312
  const borderRadiusStyle = useMemo(() => {
251
313
  if (!backgroundBorderRadius) return void 0;
252
314
  const radii = getBorderRadii(backgroundBorderRadius);
@@ -270,8 +332,10 @@ function TextElement({ segment, scale = 1 }) {
270
332
  }), [x, y, width, height, rotation, verticalAlign, autoWidth, boxAlign]);
271
333
  const backgroundBoxStyle = useMemo(() => {
272
334
  const baseStyle = autoWidth ? {
273
- width: "fit-content",
274
- maxWidth: "100%"
335
+ // Use explicit calculated width instead of fit-content
336
+ // This fixes the issue where multi-line text doesn't shrink to widest line
337
+ width: calculatedWidth,
338
+ maxWidth: width
275
339
  } : {
276
340
  // When not autoWidth, let the text div handle everything
277
341
  display: "flex",
@@ -285,7 +349,7 @@ function TextElement({ segment, scale = 1 }) {
285
349
  backgroundColor: hexToRgba(backgroundColor, backgroundOpacity),
286
350
  borderRadius: borderRadiusStyle
287
351
  };
288
- }, [autoWidth, backgroundColor, backgroundOpacity, borderRadiusStyle]);
352
+ }, [autoWidth, calculatedWidth, width, backgroundColor, backgroundOpacity, borderRadiusStyle]);
289
353
  const textStyle = useMemo(() => ({
290
354
  fontFamily,
291
355
  fontSize,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc-render",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
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",