From 0eb189fb438a56ee737547c4ee8726f262197ff1 Mon Sep 17 00:00:00 2001 From: Don Kasun Gallage Date: Mon, 28 Jul 2025 18:03:50 +0530 Subject: [PATCH 1/6] chore(release): bump version to 0.2.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5977ee2..6e114b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@donkasun/react-native-outlined-text", - "version": "0.2.2", + "version": "0.2.3", "description": "A React Native component for creating outlined text with customizable stroke effects and automatic text wrapping", "main": "lib/index.js", "types": "lib/index.d.ts", From d76d41894018364000d659cc0ba2c33cf763c75d Mon Sep 17 00:00:00 2001 From: Don Kasun Gallage Date: Mon, 28 Jul 2025 18:27:23 +0530 Subject: [PATCH 2/6] chore: update package-lock.json and refactor OutlinedText component to simplify font handling --- example/package-lock.json | 35 ++++++--------------------------- src/components/OutlinedText.tsx | 24 +++------------------- 2 files changed, 9 insertions(+), 50 deletions(-) diff --git a/example/package-lock.json b/example/package-lock.json index 8cbb679..1969665 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -22,29 +22,6 @@ "typescript": "~5.8.3" } }, - "..": { - "name": "@donkasun/react-native-outlined-text", - "version": "0.2.1", - "extraneous": true, - "license": "MIT", - "devDependencies": { - "@types/react": "^19.0.10", - "@types/react-native": "^0.72.0", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "eslint": "^8.0.0", - "eslint-plugin-react": "^7.33.0", - "eslint-plugin-react-hooks": "^4.6.0", - "prettier": "^3.0.0", - "react-native-svg": "^14.0.0", - "typescript": "^5.0.0" - }, - "peerDependencies": { - "react": ">=18.0.0", - "react-native": ">=0.60.0", - "react-native-svg": ">=12.0.0" - } - }, "node_modules/@0no-co/graphql.web": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.1.2.tgz", @@ -1519,9 +1496,9 @@ } }, "node_modules/@donkasun/react-native-outlined-text": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@donkasun/react-native-outlined-text/-/react-native-outlined-text-0.2.1.tgz", - "integrity": "sha512-FEHH/gjZdA+bv/gEDtQ2U551HHtHlTdfPoHXMIq0EUt1rln9Yt2OB+0uXCt1xc4cN4niCKHQy8mQiJn/f10VnQ==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@donkasun/react-native-outlined-text/-/react-native-outlined-text-0.2.3.tgz", + "integrity": "sha512-qjW1b536SioGInHfLKCYzZtAHolBrW7hDZ7U/eEgG1y8yc35d6X8Jo66UP7rBUGpZp9Ll5ZyKxFsfT20s+3Upg==", "license": "MIT", "peerDependencies": { "react": ">=18.0.0", @@ -6682,9 +6659,9 @@ } }, "node_modules/react-native-svg": { - "version": "15.11.2", - "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.11.2.tgz", - "integrity": "sha512-+YfF72IbWQUKzCIydlijV1fLuBsQNGMT6Da2kFlo1sh+LE3BIm/2Q7AR1zAAR6L0BFLi1WaQPLfFUC9bNZpOmw==", + "version": "15.12.0", + "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.12.0.tgz", + "integrity": "sha512-iE25PxIJ6V0C6krReLquVw6R0QTsRTmEQc4K2Co3P6zsimU/jltcDBKYDy1h/5j9S/fqmMeXnpM+9LEWKJKI6A==", "license": "MIT", "dependencies": { "css-select": "^5.1.0", diff --git a/src/components/OutlinedText.tsx b/src/components/OutlinedText.tsx index 1b51bdb..57fbd5c 100644 --- a/src/components/OutlinedText.tsx +++ b/src/components/OutlinedText.tsx @@ -2,15 +2,6 @@ import React from 'react'; import { View } from 'react-native'; import Svg, { Text as SvgText } from 'react-native-svg'; -const typography = { - fontFamily: { - regular: 'Nunito-Regular', - medium: 'Nunito-Medium', - semiBold: 'Nunito-SemiBold', - bold: 'Nunito-Bold', - }, -} as const; - interface SvgTextOutlinedProps { text: string; width: number; @@ -27,8 +18,7 @@ interface SvgTextOutlinedProps { x?: number; y?: number; textAnchor?: 'start' | 'middle' | 'end'; - fontFamily?: keyof typeof typography.fontFamily; - fontWeight?: keyof typeof typography.fontFamily; + fontFamily?: string; letterSpacing?: number; textTransform?: 'none' | 'uppercase' | 'lowercase' | 'capitalize'; textDecoration?: 'none' | 'underline' | 'line-through'; @@ -122,17 +112,12 @@ export function SvgTextOutlined({ x, y, textAnchor = 'middle', - fontFamily = 'medium', - fontWeight, + fontFamily = 'System', letterSpacing, textTransform = 'none', textDecoration = 'none', opacity = 1, }: SvgTextOutlinedProps) { - // Determine final font family - const finalFontFamily = fontWeight || fontFamily; - const finalFontFamilyValue = typography.fontFamily[finalFontFamily]; - // Apply text transformations const processedText = (() => { let result = text; @@ -185,7 +170,7 @@ export function SvgTextOutlined({ x: textX, y: lineY, textAnchor, - fontFamily: finalFontFamilyValue, + fontFamily, opacity, ...(letterSpacing && { letterSpacing }), }; @@ -253,9 +238,6 @@ export function SvgTextOutlined({ ); } -// Export the typography object for external use if needed -export { typography }; - // Export types for external use export type { SvgTextOutlinedProps }; From 30421c39db82eb6887fc499361996e06e0e4501e Mon Sep 17 00:00:00 2001 From: Don Kasun Gallage Date: Mon, 28 Jul 2025 18:49:13 +0530 Subject: [PATCH 3/6] feat: enhance font handling in OutlinedText component with platform-specific fallbacks --- src/components/OutlinedText.tsx | 45 ++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/components/OutlinedText.tsx b/src/components/OutlinedText.tsx index 57fbd5c..755906d 100644 --- a/src/components/OutlinedText.tsx +++ b/src/components/OutlinedText.tsx @@ -1,7 +1,41 @@ import React from 'react'; -import { View } from 'react-native'; +import { View, Platform } from 'react-native'; import Svg, { Text as SvgText } from 'react-native-svg'; +// Platform-specific default fonts +const getDefaultFontFamily = (): string => { + if (Platform.OS === 'ios') { + return 'System'; + } else if (Platform.OS === 'android') { + return 'Roboto'; + } + return 'Arial'; // Web fallback +}; + +// Helper function to create font fallback chain +const createFontFallback = (fontFamily?: string): string => { + if (!fontFamily) { + return getDefaultFontFamily(); + } + + // If user provides a font family, create a fallback chain + const fallbacks = []; + + // Add the user's font family + fallbacks.push(fontFamily); + + // Add platform-specific fallbacks + if (Platform.OS === 'ios') { + fallbacks.push('System', 'Helvetica Neue', 'Helvetica'); + } else if (Platform.OS === 'android') { + fallbacks.push('Roboto', 'Noto Sans', 'sans-serif'); + } else { + fallbacks.push('Arial', 'Helvetica', 'sans-serif'); + } + + return fallbacks.join(', '); +}; + interface SvgTextOutlinedProps { text: string; width: number; @@ -18,7 +52,7 @@ interface SvgTextOutlinedProps { x?: number; y?: number; textAnchor?: 'start' | 'middle' | 'end'; - fontFamily?: string; + fontFamily?: string; // Font family name. If not provided, uses platform-specific defaults with fallbacks letterSpacing?: number; textTransform?: 'none' | 'uppercase' | 'lowercase' | 'capitalize'; textDecoration?: 'none' | 'underline' | 'line-through'; @@ -112,12 +146,15 @@ export function SvgTextOutlined({ x, y, textAnchor = 'middle', - fontFamily = 'System', + fontFamily, letterSpacing, textTransform = 'none', textDecoration = 'none', opacity = 1, }: SvgTextOutlinedProps) { + // Use font fallback chain for better reliability + const finalFontFamily = createFontFallback(fontFamily); + // Apply text transformations const processedText = (() => { let result = text; @@ -170,7 +207,7 @@ export function SvgTextOutlined({ x: textX, y: lineY, textAnchor, - fontFamily, + fontFamily: finalFontFamily, opacity, ...(letterSpacing && { letterSpacing }), }; From d1680031f1a9ef834d067855ec1f6bd1296703b8 Mon Sep 17 00:00:00 2001 From: Don Kasun Gallage Date: Mon, 28 Jul 2025 19:01:21 +0530 Subject: [PATCH 4/6] feat: improve font handling in OutlinedText component with validation and enhanced width estimation --- src/components/OutlinedText.tsx | 122 +++++++++++++++++++++++++++++--- 1 file changed, 113 insertions(+), 9 deletions(-) diff --git a/src/components/OutlinedText.tsx b/src/components/OutlinedText.tsx index 755906d..45985c4 100644 --- a/src/components/OutlinedText.tsx +++ b/src/components/OutlinedText.tsx @@ -12,9 +12,54 @@ const getDefaultFontFamily = (): string => { return 'Arial'; // Web fallback }; +// Font weight mapping for better cross-platform support +const fontWeights = { + thin: '100', + '100': '100', + light: '200', + '200': '200', + '300': '300', + normal: '400', + '400': '400', + medium: '500', + '500': '500', + '600': '600', + semibold: '600', + bold: '700', + '700': '700', + '800': '800', + heavy: '800', + '900': '900', + black: '900', +} as const; + +// Font style options +const fontStyles = { + normal: 'normal', + italic: 'italic', +} as const; + +// Helper function to validate font family +const validateFontFamily = (fontFamily?: string): boolean => { + if (!fontFamily) return false; + + // Basic validation - check if it's a non-empty string + if (typeof fontFamily !== 'string' || fontFamily.trim() === '') { + return false; + } + + // Check for common invalid font names + const invalidNames = ['undefined', 'null', 'System', 'Default']; + if (invalidNames.includes(fontFamily.trim())) { + return false; + } + + return true; +}; + // Helper function to create font fallback chain const createFontFallback = (fontFamily?: string): string => { - if (!fontFamily) { + if (!fontFamily || !validateFontFamily(fontFamily)) { return getDefaultFontFamily(); } @@ -53,24 +98,70 @@ interface SvgTextOutlinedProps { y?: number; textAnchor?: 'start' | 'middle' | 'end'; fontFamily?: string; // Font family name. If not provided, uses platform-specific defaults with fallbacks + fontWeight?: keyof typeof fontWeights | string; // Font weight (100-900, 'normal', 'bold', etc.) + fontStyle?: keyof typeof fontStyles; // Font style ('normal', 'italic') letterSpacing?: number; textTransform?: 'none' | 'uppercase' | 'lowercase' | 'capitalize'; textDecoration?: 'none' | 'underline' | 'line-through'; opacity?: number; } -// Helper function to estimate text width -const estimateTextWidth = (text: string, fontSize: number): number => { - // Rough estimation: each character is approximately 0.6 * fontSize wide - // This is a simplified approach - for more accuracy, you'd need a proper text measurement library - return text.length * fontSize * 0.6; +// Helper function to estimate text width with better accuracy +const estimateTextWidth = ( + text: string, + fontSize: number, + fontFamily?: string, + fontWeight?: string, + fontStyle?: string +): number => { + // More accurate estimation based on font characteristics + let charWidth = 0.6; // Default character width ratio + + // Adjust based on font weight + if (fontWeight) { + const weight = parseInt(fontWeight); + if (weight >= 700) { + charWidth = 0.65; // Bold fonts are wider + } else if (weight <= 300) { + charWidth = 0.55; // Light fonts are narrower + } + } + + // Adjust based on font style + if (fontStyle === 'italic') { + charWidth *= 1.05; // Italic fonts are slightly wider + } + + // Adjust based on font family characteristics + if (fontFamily) { + const family = fontFamily.toLowerCase(); + if (family.includes('mono') || family.includes('courier')) { + charWidth = 0.7; // Monospace fonts have consistent width + } else if (family.includes('condensed') || family.includes('narrow')) { + charWidth *= 0.85; // Condensed fonts are narrower + } else if (family.includes('wide') || family.includes('extended')) { + charWidth *= 1.15; // Wide fonts are broader + } + } + + // Calculate total width + const baseWidth = text.length * fontSize * charWidth; + + // Add extra space for word spacing + const wordCount = text.split(' ').length - 1; + const wordSpacing = wordCount * fontSize * 0.1; + + return baseWidth + wordSpacing; }; // Helper function to wrap text into lines const wrapText = ( text: string, maxWidth: number, - fontSize: number + fontSize: number, + fontFamily?: string, + fontWeight?: string, + fontStyle?: string ): string[] => { const words = text.split(' '); const lines: string[] = []; @@ -78,7 +169,7 @@ const wrapText = ( for (const word of words) { const testLine = currentLine ? `${currentLine} ${word}` : word; - const testWidth = estimateTextWidth(testLine, fontSize); + const testWidth = estimateTextWidth(testLine, fontSize, fontFamily, fontWeight, fontStyle); if (testWidth <= maxWidth) { currentLine = testLine; @@ -147,6 +238,8 @@ export function SvgTextOutlined({ y, textAnchor = 'middle', fontFamily, + fontWeight, + fontStyle, letterSpacing, textTransform = 'none', textDecoration = 'none', @@ -154,6 +247,12 @@ export function SvgTextOutlined({ }: SvgTextOutlinedProps) { // Use font fallback chain for better reliability const finalFontFamily = createFontFallback(fontFamily); + + // Process font weight + const finalFontWeight = fontWeight ? fontWeights[fontWeight as keyof typeof fontWeights] || fontWeight : undefined; + + // Process font style + const finalFontStyle = fontStyle || 'normal'; // Apply text transformations const processedText = (() => { @@ -175,7 +274,7 @@ export function SvgTextOutlined({ })(); // Wrap text into lines - const lines = wrapText(processedText, width, fontSize); + const lines = wrapText(processedText, width, fontSize, finalFontFamily, finalFontWeight, finalFontStyle); // Calculate total height needed for all lines const totalLineHeight = fontSize * 1.2; @@ -208,6 +307,8 @@ export function SvgTextOutlined({ y: lineY, textAnchor, fontFamily: finalFontFamily, + fontWeight: finalFontWeight, + fontStyle: finalFontStyle, opacity, ...(letterSpacing && { letterSpacing }), }; @@ -275,6 +376,9 @@ export function SvgTextOutlined({ ); } +// Export font utilities for external use +export { fontWeights, fontStyles }; + // Export types for external use export type { SvgTextOutlinedProps }; From c7332e0020b363343d75fcc52f03f3af333ee2ec Mon Sep 17 00:00:00 2001 From: Don Kasun Date: Mon, 28 Jul 2025 19:03:23 +0530 Subject: [PATCH 5/6] feat: Add font weight, style support and improved text measurement (#5) * chore: update package-lock.json and refactor OutlinedText component to simplify font handling * feat: enhance font handling in OutlinedText component with platform-specific fallbacks * feat: improve font handling in OutlinedText component with validation and enhanced width estimation --- example/package-lock.json | 35 ++----- src/components/OutlinedText.tsx | 173 +++++++++++++++++++++++++++----- 2 files changed, 154 insertions(+), 54 deletions(-) diff --git a/example/package-lock.json b/example/package-lock.json index 8cbb679..1969665 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -22,29 +22,6 @@ "typescript": "~5.8.3" } }, - "..": { - "name": "@donkasun/react-native-outlined-text", - "version": "0.2.1", - "extraneous": true, - "license": "MIT", - "devDependencies": { - "@types/react": "^19.0.10", - "@types/react-native": "^0.72.0", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "eslint": "^8.0.0", - "eslint-plugin-react": "^7.33.0", - "eslint-plugin-react-hooks": "^4.6.0", - "prettier": "^3.0.0", - "react-native-svg": "^14.0.0", - "typescript": "^5.0.0" - }, - "peerDependencies": { - "react": ">=18.0.0", - "react-native": ">=0.60.0", - "react-native-svg": ">=12.0.0" - } - }, "node_modules/@0no-co/graphql.web": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.1.2.tgz", @@ -1519,9 +1496,9 @@ } }, "node_modules/@donkasun/react-native-outlined-text": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@donkasun/react-native-outlined-text/-/react-native-outlined-text-0.2.1.tgz", - "integrity": "sha512-FEHH/gjZdA+bv/gEDtQ2U551HHtHlTdfPoHXMIq0EUt1rln9Yt2OB+0uXCt1xc4cN4niCKHQy8mQiJn/f10VnQ==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@donkasun/react-native-outlined-text/-/react-native-outlined-text-0.2.3.tgz", + "integrity": "sha512-qjW1b536SioGInHfLKCYzZtAHolBrW7hDZ7U/eEgG1y8yc35d6X8Jo66UP7rBUGpZp9Ll5ZyKxFsfT20s+3Upg==", "license": "MIT", "peerDependencies": { "react": ">=18.0.0", @@ -6682,9 +6659,9 @@ } }, "node_modules/react-native-svg": { - "version": "15.11.2", - "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.11.2.tgz", - "integrity": "sha512-+YfF72IbWQUKzCIydlijV1fLuBsQNGMT6Da2kFlo1sh+LE3BIm/2Q7AR1zAAR6L0BFLi1WaQPLfFUC9bNZpOmw==", + "version": "15.12.0", + "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.12.0.tgz", + "integrity": "sha512-iE25PxIJ6V0C6krReLquVw6R0QTsRTmEQc4K2Co3P6zsimU/jltcDBKYDy1h/5j9S/fqmMeXnpM+9LEWKJKI6A==", "license": "MIT", "dependencies": { "css-select": "^5.1.0", diff --git a/src/components/OutlinedText.tsx b/src/components/OutlinedText.tsx index 1b51bdb..45985c4 100644 --- a/src/components/OutlinedText.tsx +++ b/src/components/OutlinedText.tsx @@ -1,16 +1,86 @@ import React from 'react'; -import { View } from 'react-native'; +import { View, Platform } from 'react-native'; import Svg, { Text as SvgText } from 'react-native-svg'; -const typography = { - fontFamily: { - regular: 'Nunito-Regular', - medium: 'Nunito-Medium', - semiBold: 'Nunito-SemiBold', - bold: 'Nunito-Bold', - }, +// Platform-specific default fonts +const getDefaultFontFamily = (): string => { + if (Platform.OS === 'ios') { + return 'System'; + } else if (Platform.OS === 'android') { + return 'Roboto'; + } + return 'Arial'; // Web fallback +}; + +// Font weight mapping for better cross-platform support +const fontWeights = { + thin: '100', + '100': '100', + light: '200', + '200': '200', + '300': '300', + normal: '400', + '400': '400', + medium: '500', + '500': '500', + '600': '600', + semibold: '600', + bold: '700', + '700': '700', + '800': '800', + heavy: '800', + '900': '900', + black: '900', +} as const; + +// Font style options +const fontStyles = { + normal: 'normal', + italic: 'italic', } as const; +// Helper function to validate font family +const validateFontFamily = (fontFamily?: string): boolean => { + if (!fontFamily) return false; + + // Basic validation - check if it's a non-empty string + if (typeof fontFamily !== 'string' || fontFamily.trim() === '') { + return false; + } + + // Check for common invalid font names + const invalidNames = ['undefined', 'null', 'System', 'Default']; + if (invalidNames.includes(fontFamily.trim())) { + return false; + } + + return true; +}; + +// Helper function to create font fallback chain +const createFontFallback = (fontFamily?: string): string => { + if (!fontFamily || !validateFontFamily(fontFamily)) { + return getDefaultFontFamily(); + } + + // If user provides a font family, create a fallback chain + const fallbacks = []; + + // Add the user's font family + fallbacks.push(fontFamily); + + // Add platform-specific fallbacks + if (Platform.OS === 'ios') { + fallbacks.push('System', 'Helvetica Neue', 'Helvetica'); + } else if (Platform.OS === 'android') { + fallbacks.push('Roboto', 'Noto Sans', 'sans-serif'); + } else { + fallbacks.push('Arial', 'Helvetica', 'sans-serif'); + } + + return fallbacks.join(', '); +}; + interface SvgTextOutlinedProps { text: string; width: number; @@ -27,26 +97,71 @@ interface SvgTextOutlinedProps { x?: number; y?: number; textAnchor?: 'start' | 'middle' | 'end'; - fontFamily?: keyof typeof typography.fontFamily; - fontWeight?: keyof typeof typography.fontFamily; + fontFamily?: string; // Font family name. If not provided, uses platform-specific defaults with fallbacks + fontWeight?: keyof typeof fontWeights | string; // Font weight (100-900, 'normal', 'bold', etc.) + fontStyle?: keyof typeof fontStyles; // Font style ('normal', 'italic') letterSpacing?: number; textTransform?: 'none' | 'uppercase' | 'lowercase' | 'capitalize'; textDecoration?: 'none' | 'underline' | 'line-through'; opacity?: number; } -// Helper function to estimate text width -const estimateTextWidth = (text: string, fontSize: number): number => { - // Rough estimation: each character is approximately 0.6 * fontSize wide - // This is a simplified approach - for more accuracy, you'd need a proper text measurement library - return text.length * fontSize * 0.6; +// Helper function to estimate text width with better accuracy +const estimateTextWidth = ( + text: string, + fontSize: number, + fontFamily?: string, + fontWeight?: string, + fontStyle?: string +): number => { + // More accurate estimation based on font characteristics + let charWidth = 0.6; // Default character width ratio + + // Adjust based on font weight + if (fontWeight) { + const weight = parseInt(fontWeight); + if (weight >= 700) { + charWidth = 0.65; // Bold fonts are wider + } else if (weight <= 300) { + charWidth = 0.55; // Light fonts are narrower + } + } + + // Adjust based on font style + if (fontStyle === 'italic') { + charWidth *= 1.05; // Italic fonts are slightly wider + } + + // Adjust based on font family characteristics + if (fontFamily) { + const family = fontFamily.toLowerCase(); + if (family.includes('mono') || family.includes('courier')) { + charWidth = 0.7; // Monospace fonts have consistent width + } else if (family.includes('condensed') || family.includes('narrow')) { + charWidth *= 0.85; // Condensed fonts are narrower + } else if (family.includes('wide') || family.includes('extended')) { + charWidth *= 1.15; // Wide fonts are broader + } + } + + // Calculate total width + const baseWidth = text.length * fontSize * charWidth; + + // Add extra space for word spacing + const wordCount = text.split(' ').length - 1; + const wordSpacing = wordCount * fontSize * 0.1; + + return baseWidth + wordSpacing; }; // Helper function to wrap text into lines const wrapText = ( text: string, maxWidth: number, - fontSize: number + fontSize: number, + fontFamily?: string, + fontWeight?: string, + fontStyle?: string ): string[] => { const words = text.split(' '); const lines: string[] = []; @@ -54,7 +169,7 @@ const wrapText = ( for (const word of words) { const testLine = currentLine ? `${currentLine} ${word}` : word; - const testWidth = estimateTextWidth(testLine, fontSize); + const testWidth = estimateTextWidth(testLine, fontSize, fontFamily, fontWeight, fontStyle); if (testWidth <= maxWidth) { currentLine = testLine; @@ -122,16 +237,22 @@ export function SvgTextOutlined({ x, y, textAnchor = 'middle', - fontFamily = 'medium', + fontFamily, fontWeight, + fontStyle, letterSpacing, textTransform = 'none', textDecoration = 'none', opacity = 1, }: SvgTextOutlinedProps) { - // Determine final font family - const finalFontFamily = fontWeight || fontFamily; - const finalFontFamilyValue = typography.fontFamily[finalFontFamily]; + // Use font fallback chain for better reliability + const finalFontFamily = createFontFallback(fontFamily); + + // Process font weight + const finalFontWeight = fontWeight ? fontWeights[fontWeight as keyof typeof fontWeights] || fontWeight : undefined; + + // Process font style + const finalFontStyle = fontStyle || 'normal'; // Apply text transformations const processedText = (() => { @@ -153,7 +274,7 @@ export function SvgTextOutlined({ })(); // Wrap text into lines - const lines = wrapText(processedText, width, fontSize); + const lines = wrapText(processedText, width, fontSize, finalFontFamily, finalFontWeight, finalFontStyle); // Calculate total height needed for all lines const totalLineHeight = fontSize * 1.2; @@ -185,7 +306,9 @@ export function SvgTextOutlined({ x: textX, y: lineY, textAnchor, - fontFamily: finalFontFamilyValue, + fontFamily: finalFontFamily, + fontWeight: finalFontWeight, + fontStyle: finalFontStyle, opacity, ...(letterSpacing && { letterSpacing }), }; @@ -253,8 +376,8 @@ export function SvgTextOutlined({ ); } -// Export the typography object for external use if needed -export { typography }; +// Export font utilities for external use +export { fontWeights, fontStyles }; // Export types for external use export type { SvgTextOutlinedProps }; From 774ef4fc5f533b0981028553cb9848d65ce53558 Mon Sep 17 00:00:00 2001 From: Don Kasun Gallage Date: Mon, 28 Jul 2025 19:17:08 +0530 Subject: [PATCH 6/6] fix: format code with prettier --- src/components/OutlinedText.tsx | 55 +++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/src/components/OutlinedText.tsx b/src/components/OutlinedText.tsx index 45985c4..9a9f0de 100644 --- a/src/components/OutlinedText.tsx +++ b/src/components/OutlinedText.tsx @@ -42,18 +42,18 @@ const fontStyles = { // Helper function to validate font family const validateFontFamily = (fontFamily?: string): boolean => { if (!fontFamily) return false; - + // Basic validation - check if it's a non-empty string if (typeof fontFamily !== 'string' || fontFamily.trim() === '') { return false; } - + // Check for common invalid font names const invalidNames = ['undefined', 'null', 'System', 'Default']; if (invalidNames.includes(fontFamily.trim())) { return false; } - + return true; }; @@ -62,13 +62,13 @@ const createFontFallback = (fontFamily?: string): string => { if (!fontFamily || !validateFontFamily(fontFamily)) { return getDefaultFontFamily(); } - + // If user provides a font family, create a fallback chain const fallbacks = []; - + // Add the user's font family fallbacks.push(fontFamily); - + // Add platform-specific fallbacks if (Platform.OS === 'ios') { fallbacks.push('System', 'Helvetica Neue', 'Helvetica'); @@ -77,7 +77,7 @@ const createFontFallback = (fontFamily?: string): string => { } else { fallbacks.push('Arial', 'Helvetica', 'sans-serif'); } - + return fallbacks.join(', '); }; @@ -108,15 +108,15 @@ interface SvgTextOutlinedProps { // Helper function to estimate text width with better accuracy const estimateTextWidth = ( - text: string, - fontSize: number, + text: string, + fontSize: number, fontFamily?: string, fontWeight?: string, fontStyle?: string ): number => { // More accurate estimation based on font characteristics let charWidth = 0.6; // Default character width ratio - + // Adjust based on font weight if (fontWeight) { const weight = parseInt(fontWeight); @@ -126,12 +126,12 @@ const estimateTextWidth = ( charWidth = 0.55; // Light fonts are narrower } } - + // Adjust based on font style if (fontStyle === 'italic') { charWidth *= 1.05; // Italic fonts are slightly wider } - + // Adjust based on font family characteristics if (fontFamily) { const family = fontFamily.toLowerCase(); @@ -143,14 +143,14 @@ const estimateTextWidth = ( charWidth *= 1.15; // Wide fonts are broader } } - + // Calculate total width const baseWidth = text.length * fontSize * charWidth; - + // Add extra space for word spacing const wordCount = text.split(' ').length - 1; const wordSpacing = wordCount * fontSize * 0.1; - + return baseWidth + wordSpacing; }; @@ -169,7 +169,13 @@ const wrapText = ( for (const word of words) { const testLine = currentLine ? `${currentLine} ${word}` : word; - const testWidth = estimateTextWidth(testLine, fontSize, fontFamily, fontWeight, fontStyle); + const testWidth = estimateTextWidth( + testLine, + fontSize, + fontFamily, + fontWeight, + fontStyle + ); if (testWidth <= maxWidth) { currentLine = testLine; @@ -247,10 +253,12 @@ export function SvgTextOutlined({ }: SvgTextOutlinedProps) { // Use font fallback chain for better reliability const finalFontFamily = createFontFallback(fontFamily); - + // Process font weight - const finalFontWeight = fontWeight ? fontWeights[fontWeight as keyof typeof fontWeights] || fontWeight : undefined; - + const finalFontWeight = fontWeight + ? fontWeights[fontWeight as keyof typeof fontWeights] || fontWeight + : undefined; + // Process font style const finalFontStyle = fontStyle || 'normal'; @@ -274,7 +282,14 @@ export function SvgTextOutlined({ })(); // Wrap text into lines - const lines = wrapText(processedText, width, fontSize, finalFontFamily, finalFontWeight, finalFontStyle); + const lines = wrapText( + processedText, + width, + fontSize, + finalFontFamily, + finalFontWeight, + finalFontStyle + ); // Calculate total height needed for all lines const totalLineHeight = fontSize * 1.2;