diff --git a/src/constants/code/TextAnimations/textLoopCode.js b/src/constants/code/TextAnimations/textLoopCode.js index 82d1bacab..236846702 100644 --- a/src/constants/code/TextAnimations/textLoopCode.js +++ b/src/constants/code/TextAnimations/textLoopCode.js @@ -9,7 +9,7 @@ export const textLoop = { usage: `import TextLoop from './TextLoop'; { }; const TextLoop = ({ - text = 'React ✦ Bits', + text = 'React Bits', shape = 'wave', path, speed = 90, @@ -78,8 +78,22 @@ const TextLoop = ({ const d = useMemo(() => path || buildPath(shape, curviness, ribbonWidth), [path, shape, curviness, ribbonWidth]); const unit = useMemo(() => { - const base = uppercase ? String(text).toUpperCase() : String(text); - const gap = separator ? `\u00A0${separator}\u00A0` : '\u00A0\u00A0\u00A0'; + let base = String(text ?? ''); + let sep = separator ?? ''; + if (uppercase) { + base = base.toUpperCase(); + sep = sep.toUpperCase(); + } + if (sep) { + // Strip any separator glyphs already present in the text so each + // repetition is joined by exactly one separator. A duplicated glyph + // breaks the textLength/lengthAdjust fit on the SVG path in stricter + // browsers (e.g. Firefox) and a separator can land on top of a letter. + base = base.split(sep).join('').replace(/[\s\u00A0]+/g, ' ').trim(); + } else { + base = base.replace(/[\s\u00A0]+/g, ' ').trim(); + } + const gap = sep ? `\u00A0${sep}\u00A0` : '\u00A0\u00A0\u00A0'; return `${base}${gap}`; }, [text, separator, uppercase]); diff --git a/src/demo/TextAnimations/TextLoopDemo.jsx b/src/demo/TextAnimations/TextLoopDemo.jsx index ab25a701f..24f01aa7d 100644 --- a/src/demo/TextAnimations/TextLoopDemo.jsx +++ b/src/demo/TextAnimations/TextLoopDemo.jsx @@ -19,7 +19,7 @@ import TextLoop from '../../content/TextAnimations/TextLoop/TextLoop'; import { textLoop } from '../../constants/code/TextAnimations/textLoopCode'; const DEFAULT_PROPS = { - text: 'React ✦ Bits', + text: 'React Bits', shape: 'wave', speed: 90, direction: 'forward', @@ -72,7 +72,7 @@ const TextLoopDemo = () => { const propData = useMemo( () => [ - { name: 'text', type: 'string', default: '"React ✦ Bits"', description: 'The phrase repeated along the curve.' }, + { name: 'text', type: 'string', default: '"React Bits"', description: 'The phrase repeated along the curve.' }, { name: 'shape', type: '"wave" | "circle" | "infinity" | "arch" | "line"', diff --git a/src/tailwind/TextAnimations/TextLoop/TextLoop.jsx b/src/tailwind/TextAnimations/TextLoop/TextLoop.jsx index 39c7adea1..c2e9151d0 100644 --- a/src/tailwind/TextAnimations/TextLoop/TextLoop.jsx +++ b/src/tailwind/TextAnimations/TextLoop/TextLoop.jsx @@ -43,7 +43,7 @@ const buildPath = (shape, curviness, ribbonWidth) => { }; const TextLoop = ({ - text = 'React ✦ Bits', + text = 'React Bits', shape = 'wave', path, speed = 90, @@ -76,8 +76,22 @@ const TextLoop = ({ const d = useMemo(() => path || buildPath(shape, curviness, ribbonWidth), [path, shape, curviness, ribbonWidth]); const unit = useMemo(() => { - const base = uppercase ? String(text).toUpperCase() : String(text); - const gap = separator ? `\u00A0${separator}\u00A0` : '\u00A0\u00A0\u00A0'; + let base = String(text ?? ''); + let sep = separator ?? ''; + if (uppercase) { + base = base.toUpperCase(); + sep = sep.toUpperCase(); + } + if (sep) { + // Strip any separator glyphs already present in the text so each + // repetition is joined by exactly one separator. A duplicated glyph + // breaks the textLength/lengthAdjust fit on the SVG path in stricter + // browsers (e.g. Firefox) and a separator can land on top of a letter. + base = base.split(sep).join('').replace(/[\s\u00A0]+/g, ' ').trim(); + } else { + base = base.replace(/[\s\u00A0]+/g, ' ').trim(); + } + const gap = sep ? `\u00A0${sep}\u00A0` : '\u00A0\u00A0\u00A0'; return `${base}${gap}`; }, [text, separator, uppercase]); diff --git a/src/ts-default/TextAnimations/TextLoop/TextLoop.tsx b/src/ts-default/TextAnimations/TextLoop/TextLoop.tsx index 9952afac8..a580e10ed 100644 --- a/src/ts-default/TextAnimations/TextLoop/TextLoop.tsx +++ b/src/ts-default/TextAnimations/TextLoop/TextLoop.tsx @@ -74,7 +74,7 @@ const buildPath = (shape: TextLoopShape, curviness: number, ribbonWidth: number) }; const TextLoop = ({ - text = 'React ✦ Bits', + text = 'React Bits', shape = 'wave', path, speed = 90, @@ -107,8 +107,22 @@ const TextLoop = ({ const d = useMemo(() => path || buildPath(shape, curviness, ribbonWidth), [path, shape, curviness, ribbonWidth]); const unit = useMemo(() => { - const base = uppercase ? String(text).toUpperCase() : String(text); - const gap = separator ? `\u00A0${separator}\u00A0` : '\u00A0\u00A0\u00A0'; + let base = String(text ?? ''); + let sep = separator ?? ''; + if (uppercase) { + base = base.toUpperCase(); + sep = sep.toUpperCase(); + } + if (sep) { + // Strip any separator glyphs already present in the text so each + // repetition is joined by exactly one separator. A duplicated glyph + // breaks the textLength/lengthAdjust fit on the SVG path in stricter + // browsers (e.g. Firefox) and a separator can land on top of a letter. + base = base.split(sep).join('').replace(/[\s\u00A0]+/g, ' ').trim(); + } else { + base = base.replace(/[\s\u00A0]+/g, ' ').trim(); + } + const gap = sep ? `\u00A0${sep}\u00A0` : '\u00A0\u00A0\u00A0'; return `${base}${gap}`; }, [text, separator, uppercase]); diff --git a/src/ts-tailwind/TextAnimations/TextLoop/TextLoop.tsx b/src/ts-tailwind/TextAnimations/TextLoop/TextLoop.tsx index 6d474a590..7f5e809ab 100644 --- a/src/ts-tailwind/TextAnimations/TextLoop/TextLoop.tsx +++ b/src/ts-tailwind/TextAnimations/TextLoop/TextLoop.tsx @@ -72,7 +72,7 @@ const buildPath = (shape: TextLoopShape, curviness: number, ribbonWidth: number) }; const TextLoop = ({ - text = 'React ✦ Bits', + text = 'React Bits', shape = 'wave', path, speed = 90, @@ -105,8 +105,22 @@ const TextLoop = ({ const d = useMemo(() => path || buildPath(shape, curviness, ribbonWidth), [path, shape, curviness, ribbonWidth]); const unit = useMemo(() => { - const base = uppercase ? String(text).toUpperCase() : String(text); - const gap = separator ? `\u00A0${separator}\u00A0` : '\u00A0\u00A0\u00A0'; + let base = String(text ?? ''); + let sep = separator ?? ''; + if (uppercase) { + base = base.toUpperCase(); + sep = sep.toUpperCase(); + } + if (sep) { + // Strip any separator glyphs already present in the text so each + // repetition is joined by exactly one separator. A duplicated glyph + // breaks the textLength/lengthAdjust fit on the SVG path in stricter + // browsers (e.g. Firefox) and a separator can land on top of a letter. + base = base.split(sep).join('').replace(/[\s\u00A0]+/g, ' ').trim(); + } else { + base = base.replace(/[\s\u00A0]+/g, ' ').trim(); + } + const gap = sep ? `\u00A0${sep}\u00A0` : '\u00A0\u00A0\u00A0'; return `${base}${gap}`; }, [text, separator, uppercase]);