ugcinc 4.5.72 → 4.5.74
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.
|
@@ -15,12 +15,16 @@ export interface TextElementProps {
|
|
|
15
15
|
* - Font family, size, weight, color
|
|
16
16
|
* - Letter spacing, line height
|
|
17
17
|
* - Horizontal and vertical alignment
|
|
18
|
-
* - Text stroke/outline
|
|
18
|
+
* - Text stroke/outline (via -webkit-text-stroke + paint-order)
|
|
19
19
|
* - Background with opacity and border radius
|
|
20
20
|
* - Padding (uniform and individual)
|
|
21
21
|
* - Auto-width with box alignment
|
|
22
22
|
* - Rotation
|
|
23
23
|
*
|
|
24
|
+
* Stroke rendering uses -webkit-text-stroke with paint-order: stroke fill.
|
|
25
|
+
* This naturally excludes emoji bitmaps from the outline (only text glyphs
|
|
26
|
+
* are stroked) and avoids the layout issues of a separate stroke layer.
|
|
27
|
+
*
|
|
24
28
|
* When autoWidth is enabled:
|
|
25
29
|
* - Text wraps at maxWidth (the element's width)
|
|
26
30
|
* - Background shrinks to the widest line
|
|
@@ -45,7 +45,6 @@ const react_1 = __importStar(require("react"));
|
|
|
45
45
|
const defaults_1 = require("../utils/defaults");
|
|
46
46
|
const fonts_1 = require("../utils/fonts");
|
|
47
47
|
const text_1 = require("../utils/text");
|
|
48
|
-
const emoji_1 = require("../utils/emoji");
|
|
49
48
|
/**
|
|
50
49
|
* Calculate the actual width for auto-width text AND the line breaks.
|
|
51
50
|
* Uses DOM-based measurement to ensure the same fonts are used as CSS rendering.
|
|
@@ -162,41 +161,21 @@ function calculateAutoWidthAndLines({ text, maxWidth, paddingLeft, paddingRight,
|
|
|
162
161
|
const calculatedWidth = Math.min(widestLineWidth + paddingLeft + paddingRight, maxWidth);
|
|
163
162
|
return { width: calculatedWidth, lines };
|
|
164
163
|
}
|
|
165
|
-
/** Shared inline style for emoji spans — prevents line breaks */
|
|
166
|
-
const emojiSpanStyle = {
|
|
167
|
-
display: 'inline',
|
|
168
|
-
whiteSpace: 'pre-wrap',
|
|
169
|
-
};
|
|
170
|
-
/** Stroke layer emoji span — same structure as fill but invisible to the SVG filter */
|
|
171
|
-
const emojiSpanStrokeStyle = {
|
|
172
|
-
...emojiSpanStyle,
|
|
173
|
-
opacity: 0,
|
|
174
|
-
};
|
|
175
|
-
/**
|
|
176
|
-
* Render text with emojis wrapped in spans.
|
|
177
|
-
* Both fill and stroke divs use this so they have identical DOM structure
|
|
178
|
-
* (matching vertical metrics). The only difference is the emoji span style.
|
|
179
|
-
*/
|
|
180
|
-
function renderTextWithEmojiSpans(text, forStroke) {
|
|
181
|
-
if (!(0, emoji_1.hasEmoji)(text))
|
|
182
|
-
return text;
|
|
183
|
-
const parts = (0, emoji_1.splitTextAndEmojis)(text);
|
|
184
|
-
const style = forStroke ? emojiSpanStrokeStyle : emojiSpanStyle;
|
|
185
|
-
return parts.map((part, i) => part.type === 'emoji'
|
|
186
|
-
? (0, jsx_runtime_1.jsx)("span", { style: style, children: part.content }, i)
|
|
187
|
-
: part.content);
|
|
188
|
-
}
|
|
189
164
|
/**
|
|
190
165
|
* TextElement renders text with full styling support including:
|
|
191
166
|
* - Font family, size, weight, color
|
|
192
167
|
* - Letter spacing, line height
|
|
193
168
|
* - Horizontal and vertical alignment
|
|
194
|
-
* - Text stroke/outline
|
|
169
|
+
* - Text stroke/outline (via -webkit-text-stroke + paint-order)
|
|
195
170
|
* - Background with opacity and border radius
|
|
196
171
|
* - Padding (uniform and individual)
|
|
197
172
|
* - Auto-width with box alignment
|
|
198
173
|
* - Rotation
|
|
199
174
|
*
|
|
175
|
+
* Stroke rendering uses -webkit-text-stroke with paint-order: stroke fill.
|
|
176
|
+
* This naturally excludes emoji bitmaps from the outline (only text glyphs
|
|
177
|
+
* are stroked) and avoids the layout issues of a separate stroke layer.
|
|
178
|
+
*
|
|
200
179
|
* When autoWidth is enabled:
|
|
201
180
|
* - Text wraps at maxWidth (the element's width)
|
|
202
181
|
* - Background shrinks to the widest line
|
|
@@ -344,8 +323,7 @@ function TextElement({ segment, scale = 1 }) {
|
|
|
344
323
|
};
|
|
345
324
|
}, [autoWidth, calculatedWidth, width, backgroundColor, backgroundOpacity, borderRadiusStyle]);
|
|
346
325
|
const hasStroke = strokeWidth > 0 && !!strokeColor;
|
|
347
|
-
|
|
348
|
-
// Text style (fill layer)
|
|
326
|
+
// Text style — single div handles both fill and stroke via paint-order
|
|
349
327
|
const textStyle = (0, react_1.useMemo)(() => ({
|
|
350
328
|
fontFamily,
|
|
351
329
|
fontSize,
|
|
@@ -364,45 +342,27 @@ function TextElement({ segment, scale = 1 }) {
|
|
|
364
342
|
flex: 1,
|
|
365
343
|
minHeight: 0,
|
|
366
344
|
}),
|
|
367
|
-
...(hasStroke && {
|
|
345
|
+
...(hasStroke && {
|
|
346
|
+
WebkitTextStroke: `${strokeWidth}px ${strokeColor}`,
|
|
347
|
+
paintOrder: 'stroke fill',
|
|
348
|
+
}),
|
|
368
349
|
}), [
|
|
369
350
|
fontFamily, fontSize, fontWeight, color, lineHeight, letterSpacing, alignment,
|
|
370
351
|
paddingTop, paddingRight, paddingBottom, paddingLeft,
|
|
371
|
-
autoWidth, verticalAlign, hasStroke
|
|
352
|
+
autoWidth, verticalAlign, hasStroke, strokeWidth, strokeColor
|
|
372
353
|
]);
|
|
373
|
-
// Stroke underlay style — same size as fill div, SVG filter handles overflow.
|
|
374
|
-
const strokeTextStyle = (0, react_1.useMemo)(() => {
|
|
375
|
-
if (!hasStroke)
|
|
376
|
-
return undefined;
|
|
377
|
-
return {
|
|
378
|
-
...textStyle,
|
|
379
|
-
color: strokeColor,
|
|
380
|
-
filter: `url(#${filterId})`,
|
|
381
|
-
position: 'absolute',
|
|
382
|
-
zIndex: 0,
|
|
383
|
-
top: 0,
|
|
384
|
-
left: 0,
|
|
385
|
-
right: 0,
|
|
386
|
-
bottom: 0,
|
|
387
|
-
};
|
|
388
|
-
}, [hasStroke, textStyle, strokeColor, filterId]);
|
|
389
|
-
// SVG filter for smooth text outline (avoids miter spikes from -webkit-text-stroke)
|
|
390
|
-
const strokePadPct = Math.max(50, Math.ceil((strokeWidth / fontSize) * 200));
|
|
391
|
-
const strokeFilterSvg = hasStroke ? ((0, jsx_runtime_1.jsx)("svg", { style: { position: 'absolute', width: 0, height: 0, overflow: 'hidden' }, "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("defs", { children: (0, jsx_runtime_1.jsxs)("filter", { id: filterId, x: `-${strokePadPct}%`, y: `-${strokePadPct}%`, width: `${100 + 2 * strokePadPct}%`, height: `${100 + 2 * strokePadPct}%`, children: [(0, jsx_runtime_1.jsx)("feMorphology", { in: "SourceAlpha", operator: "dilate", radius: strokeWidth, result: "dilated" }), (0, jsx_runtime_1.jsx)("feFlood", { floodColor: strokeColor, result: "color" }), (0, jsx_runtime_1.jsx)("feComposite", { in: "color", in2: "dilated", operator: "in" })] }) }) })) : null;
|
|
392
354
|
if (autoWidth) {
|
|
393
|
-
const textContent = calculatedLines.map((line, index) => ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [
|
|
394
|
-
const strokeContent = calculatedLines.map((line, index) => ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [renderTextWithEmojiSpans(line, true), index < calculatedLines.length - 1 && (0, jsx_runtime_1.jsx)("br", {})] }, index)));
|
|
355
|
+
const textContent = calculatedLines.map((line, index) => ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [line, index < calculatedLines.length - 1 && (0, jsx_runtime_1.jsx)("br", {})] }, index)));
|
|
395
356
|
if (backgroundColor) {
|
|
396
|
-
return ((0, jsx_runtime_1.jsx)("div", { style: positioningContainerStyle, children: (0, jsx_runtime_1.
|
|
357
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: positioningContainerStyle, children: (0, jsx_runtime_1.jsx)("div", { style: {
|
|
397
358
|
width: calculatedWidth,
|
|
398
359
|
maxWidth: width,
|
|
399
360
|
backgroundColor: (0, text_1.hexToRgba)(backgroundColor, backgroundOpacity),
|
|
400
361
|
borderRadius: borderRadiusStyle,
|
|
401
|
-
|
|
402
|
-
}, children: [strokeFilterSvg, strokeTextStyle && (0, jsx_runtime_1.jsx)("div", { style: strokeTextStyle, "aria-hidden": "true", children: strokeContent }), (0, jsx_runtime_1.jsx)("div", { style: textStyle, children: textContent })] }) }));
|
|
362
|
+
}, children: (0, jsx_runtime_1.jsx)("div", { style: textStyle, children: textContent }) }) }));
|
|
403
363
|
}
|
|
404
|
-
return ((0, jsx_runtime_1.jsx)("div", { style: positioningContainerStyle, children: (0, jsx_runtime_1.
|
|
364
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: positioningContainerStyle, children: (0, jsx_runtime_1.jsx)("div", { style: { width: calculatedWidth, maxWidth: width }, children: (0, jsx_runtime_1.jsx)("div", { style: textStyle, children: textContent }) }) }));
|
|
405
365
|
}
|
|
406
|
-
return ((0, jsx_runtime_1.jsx)("div", { style: positioningContainerStyle, children: (0, jsx_runtime_1.
|
|
366
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: positioningContainerStyle, children: (0, jsx_runtime_1.jsx)("div", { style: backgroundBoxStyle, children: (0, jsx_runtime_1.jsx)("div", { style: textStyle, children: segment.text }) }) }));
|
|
407
367
|
}
|
|
408
368
|
exports.default = TextElement;
|