Skip to content

feat: add textLength prop to TextPath TSpan #2414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/test-examples/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Test1318 from './src/Test1318';
import Test1374 from './src/Test1374';
import Test1442 from './src/Test1442';
import Test1451 from './src/Test1451';
import Test1693 from './src/Test1693';
import Test1718 from './src/Test1718';
import Test1790 from './src/Test1790';
import Test1813 from './src/Test1813';
Expand Down
47 changes: 47 additions & 0 deletions apps/test-examples/src/Test1693.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import {View} from 'react-native';
import {Defs, G, Path, Svg, Text, TextPath, TSpan} from 'react-native-svg';

export default function App() {
return (
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<Svg viewBox="0 -50 200 200">
<Text x="0" textLength="6em">
Text length 6em
</Text>
<Text x="0" y="15" textLength="18em">
Text length 18em
</Text>
<Text>
<TSpan x="0" y="30" textLength="8em">
Text length 8em
</TSpan>
</Text>
<Text>
<TSpan x="0" y="45" fontWeight="bold" textLength="10em">
Text Length 10em
</TSpan>
</Text>
<Defs>
<Path id="path" d="M10 50 H290" />
</Defs>
<G x="-10" y="60">
<Text fill="blue">
<TextPath href="#path">
We go up and down,
<TSpan fill="red" textLength="15em">
then up again
</TSpan>
</TextPath>
</Text>
<Path d="M10 50 H290" stroke="red" strokeWidth="1" />
</G>
</Svg>
</View>
);
}
1 change: 1 addition & 0 deletions src/elements/TSpan.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface TSpanProps extends CommonPathProps, FontProps {
dy?: NumberArray;
rotate?: NumberArray;
inlineSize?: NumberProp;
textLength?: NumberProp;
}

export default class TSpan extends Shape<TSpanProps> {
Expand Down
1 change: 1 addition & 0 deletions src/elements/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface TextProps extends TextSpecificProps {
rotate?: NumberArray;
opacity?: NumberProp;
inlineSize?: NumberProp;
textLength?: NumberProp;
}

export default class Text extends Shape<TextProps> {
Expand Down
3 changes: 3 additions & 0 deletions src/elements/TextPath.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface TextPathProps extends TextSpecificProps {
spacing?: TextPathSpacing;
midLine?: TextPathMidLine;
side?: string;
textLength?: NumberProp;
}

export default class TextPath extends Shape<TextPathProps> {
Expand Down Expand Up @@ -57,6 +58,7 @@ export default class TextPath extends Shape<TextPathProps> {
side,
alignmentBaseline,
midLine,
textLength,
...prop
} = this.props;
const matched = href && href.match(idPattern);
Expand All @@ -68,6 +70,7 @@ export default class TextPath extends Shape<TextPathProps> {
extractText(
{
children,
textLength,
},
true
),
Expand Down
5 changes: 5 additions & 0 deletions src/lib/extract/extractProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export default function extractProps(
testID?: string;
accessibilityLabel?: string;
accessible?: boolean;
textLength: string;
} & TransformProps &
ResponderProps &
StrokeProps &
Expand All @@ -78,6 +79,7 @@ export default function extractProps(
testID,
accessibilityLabel,
accessible,
textLength,
} = props;
const extracted: extractedProps = {};

Expand Down Expand Up @@ -180,6 +182,9 @@ export default function extractProps(
);
}
}
if (textLength) {
extracted.textLength = textLength;
}

return extracted;
}
Expand Down
56 changes: 42 additions & 14 deletions src/lib/extract/extractText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export function extractFont(props: fontProps) {
return { ...baseFont, ...ownedFont };
}

let TSpan: ComponentType<React.PropsWithChildren>;
let TSpan: ComponentType<React.PropsWithChildren<{ textLength?: NumberProp }>>;

export function setTSpan(TSpanImplementation: ComponentType) {
TSpan = TSpanImplementation;
Expand All @@ -124,9 +124,9 @@ export type TextChild =
| (undefined | string | number | ComponentType | React.ReactElement)
| TextChild[];

function getChild(child: TextChild) {
function getChild(child: TextChild, textLength: NumberProp | undefined) {
if (typeof child === 'string' || typeof child === 'number') {
return <TSpan>{String(child)}</TSpan>;
return <TSpan textLength={textLength}>{String(child)}</TSpan>;
} else {
return child;
}
Expand All @@ -143,8 +143,45 @@ export type TextProps = {
baselineShift?: NumberProp;
verticalAlign?: NumberProp;
alignmentBaseline?: string;
textLength?: NumberProp;
} & fontProps;

function getTextChildren(
children: TextChild,
textLength: NumberProp | undefined,
container: boolean
) {
let textChildren;
if (typeof children === 'string' || typeof children === 'number') {
if (container) {
textChildren = <TSpan textLength={textLength}>{String(children)}</TSpan>;
} else {
textChildren = null;
}
} else {
if (Children.count(children) > 1 || Array.isArray(children)) {
textChildren = Children.map(children, (child: TextChild) => {
if (React.isValidElement<TextProps>(child)) {
return React.cloneElement(child, {
textLength: child.props.textLength || textLength,
});
} else {
return getChild(child, textLength);
}
});
} else {
if (React.isValidElement<TextProps>(children)) {
textChildren = React.cloneElement(children, {
textLength: children.props.textLength || textLength,
});
} else {
textChildren = children;
}
}
}
return textChildren;
}

export default function extractText(props: TextProps, container: boolean) {
const {
x,
Expand All @@ -157,19 +194,10 @@ export default function extractText(props: TextProps, container: boolean) {
baselineShift,
verticalAlign,
alignmentBaseline,
textLength,
} = props;

const textChildren =
typeof children === 'string' || typeof children === 'number' ? (
container ? (
<TSpan>{String(children)}</TSpan>
) : null
) : Children.count(children) > 1 || Array.isArray(children) ? (
Children.map(children, getChild)
) : (
children
);

const textChildren = getTextChildren(children, textLength, container);
return {
content: textChildren === null ? String(children) : null,
children: textChildren,
Expand Down
Loading