@whetware/react-native-stroke-text 0.0.2 → 0.0.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @whetware/react-native-stroke-text
2
2
 
3
+ ## 0.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`dd724fd`](https://github.com/whetware/react-native-stroke-text/commit/dd724fd18055ec28e173029a6d3aa0a5744af919) Thanks [@evelant](https://github.com/evelant)! - fix bounding boxes
8
+
3
9
  ## 0.0.2
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -39,6 +39,12 @@ export function Example() {
39
39
  }
40
40
  ```
41
41
 
42
+ ## Layout notes
43
+
44
+ To avoid the outline getting clipped during animations (especially on Android), `StrokeText` keeps the
45
+ stroke inside the component bounds by applying an internal inset of `ceil(strokeWidth) / 2`. It then
46
+ uses matching negative margins so the layout footprint matches a normal `<Text />`.
47
+
42
48
  ## Development
43
49
 
44
50
  - Generate Nitro bindings: `pnpm specs`
@@ -113,7 +113,7 @@ internal class StrokeTextView(context: ThemedReactContext) : TextView(context) {
113
113
  }
114
114
 
115
115
  private fun applyProps() {
116
- // Padding: apply stroke inset in native to compensate for the overlay expansion in JS.
116
+ // Padding: apply a stroke inset so the outline stays within the view bounds.
117
117
  val inset = strokeInsetPx()
118
118
  val left = floorToInt(resolvePadding(paddingLeftPx, paddingHorizontalPx, paddingAllPx) + inset)
119
119
  val top = floorToInt(resolvePadding(paddingTopPx, paddingVerticalPx, paddingAllPx) + inset)
package/lib/StrokeText.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { StyleSheet, Text, View } from 'react-native';
2
+ import { I18nManager, StyleSheet, Text, View } from 'react-native';
3
3
  import { callback, getHostComponent } from 'react-native-nitro-modules';
4
4
  import StrokeTextViewConfig from '../nitrogen/generated/shared/json/StrokeTextViewConfig.json';
5
5
  const NativeStrokeTextView = getHostComponent('StrokeTextView', () => StrokeTextViewConfig);
@@ -63,6 +63,24 @@ export function StrokeText({ text, children, style, hybridRef, ...rest }) {
63
63
  const baseRight = firstNumber(paddingRight, stylePaddingRight, paddingHorizontal, stylePaddingHorizontal, padding, stylePadding) ?? 0;
64
64
  const baseBottom = firstNumber(paddingBottom, stylePaddingBottom, paddingVertical, stylePaddingVertical, padding, stylePadding) ?? 0;
65
65
  const baseLeft = firstNumber(paddingLeft, stylePaddingLeft, paddingHorizontal, stylePaddingHorizontal, padding, stylePadding) ?? 0;
66
+ const baseMarginTop = firstNumber(containerStyle.marginTop, containerStyle.marginVertical, containerStyle.margin) ?? 0;
67
+ const baseMarginRight = firstNumber(containerStyle.marginRight, I18nManager.isRTL ? containerStyle.marginStart : containerStyle.marginEnd, containerStyle.marginHorizontal, containerStyle.margin) ?? 0;
68
+ const baseMarginBottom = firstNumber(containerStyle.marginBottom, containerStyle.marginVertical, containerStyle.margin) ?? 0;
69
+ const baseMarginLeft = firstNumber(containerStyle.marginLeft, I18nManager.isRTL ? containerStyle.marginEnd : containerStyle.marginStart, containerStyle.marginHorizontal, containerStyle.margin) ?? 0;
70
+ const baseMarginStart = toNumber(containerStyle.marginStart);
71
+ const baseMarginEnd = toNumber(containerStyle.marginEnd);
72
+ const strokeInsetMarginStyle = strokeInset === 0
73
+ ? null
74
+ : {
75
+ marginTop: baseMarginTop - strokeInset,
76
+ marginRight: baseMarginRight - strokeInset,
77
+ marginBottom: baseMarginBottom - strokeInset,
78
+ marginLeft: baseMarginLeft - strokeInset,
79
+ ...(baseMarginStart == null
80
+ ? {}
81
+ : { marginStart: baseMarginStart - strokeInset }),
82
+ ...(baseMarginEnd == null ? {} : { marginEnd: baseMarginEnd - strokeInset }),
83
+ };
66
84
  const effectiveNumberOfLines = nativeProps.numberOfLines != null && nativeProps.numberOfLines > 0
67
85
  ? nativeProps.numberOfLines
68
86
  : undefined;
@@ -70,31 +88,21 @@ export function StrokeText({ text, children, style, hybridRef, ...rest }) {
70
88
  ? undefined
71
89
  : nativeProps.ellipsizeMode ?? 'tail';
72
90
  const effectiveIncludeFontPadding = nativeProps.includeFontPadding ?? styleIncludeFontPadding ?? false;
73
- return (React.createElement(View, { style: [styles.container, containerStyle] },
91
+ return (React.createElement(View, { style: [styles.container, containerStyle, strokeInsetMarginStyle] },
74
92
  React.createElement(Text, { accessible: false, pointerEvents: "none", numberOfLines: effectiveNumberOfLines, ellipsizeMode: effectiveEllipsizeMode, allowFontScaling: nativeProps.allowFontScaling, maxFontSizeMultiplier: nativeProps.maxFontSizeMultiplier, style: [
75
93
  style,
76
94
  {
77
- paddingTop: baseTop,
78
- paddingRight: baseRight,
79
- paddingBottom: baseBottom,
80
- paddingLeft: baseLeft,
95
+ paddingTop: baseTop + strokeInset,
96
+ paddingRight: baseRight + strokeInset,
97
+ paddingBottom: baseBottom + strokeInset,
98
+ paddingLeft: baseLeft + strokeInset,
81
99
  },
82
100
  effectiveIncludeFontPadding == null
83
101
  ? null
84
102
  : { includeFontPadding: effectiveIncludeFontPadding },
85
103
  styles.hiddenText,
86
104
  ] }, resolvedText),
87
- React.createElement(NativeStrokeTextView, { ...nativeProps, text: resolvedText, color: nativeProps.color ?? toColorString(styleColor), fontSize: nativeProps.fontSize ?? toNumber(styleFontSize), fontWeight: nativeProps.fontWeight ?? toFontWeightString(styleFontWeight), fontFamily: nativeProps.fontFamily ?? styleFontFamily, fontStyle: nativeProps.fontStyle ?? styleFontStyle, lineHeight: nativeProps.lineHeight ?? toNumber(styleLineHeight), letterSpacing: nativeProps.letterSpacing ?? toNumber(styleLetterSpacing), textAlign: nativeProps.textAlign ?? styleTextAlign, textDecorationLine: nativeProps.textDecorationLine ?? styleTextDecorationLine, textTransform: nativeProps.textTransform ?? styleTextTransform, opacity: nativeProps.opacity ?? toNumber(styleOpacity), includeFontPadding: effectiveIncludeFontPadding, numberOfLines: nativeProps.numberOfLines, ellipsizeMode: effectiveEllipsizeMode, paddingTop: baseTop, paddingRight: baseRight, paddingBottom: baseBottom, paddingLeft: baseLeft, hybridRef: hybridRef ? callback(hybridRef) : undefined, pointerEvents: "none", style: [
88
- styles.overlay,
89
- strokeInset === 0
90
- ? null
91
- : {
92
- top: -strokeInset,
93
- right: -strokeInset,
94
- bottom: -strokeInset,
95
- left: -strokeInset,
96
- },
97
- ] })));
105
+ React.createElement(NativeStrokeTextView, { ...nativeProps, text: resolvedText, color: nativeProps.color ?? toColorString(styleColor), fontSize: nativeProps.fontSize ?? toNumber(styleFontSize), fontWeight: nativeProps.fontWeight ?? toFontWeightString(styleFontWeight), fontFamily: nativeProps.fontFamily ?? styleFontFamily, fontStyle: nativeProps.fontStyle ?? styleFontStyle, lineHeight: nativeProps.lineHeight ?? toNumber(styleLineHeight), letterSpacing: nativeProps.letterSpacing ?? toNumber(styleLetterSpacing), textAlign: nativeProps.textAlign ?? styleTextAlign, textDecorationLine: nativeProps.textDecorationLine ?? styleTextDecorationLine, textTransform: nativeProps.textTransform ?? styleTextTransform, opacity: nativeProps.opacity ?? toNumber(styleOpacity), includeFontPadding: effectiveIncludeFontPadding, numberOfLines: nativeProps.numberOfLines, ellipsizeMode: effectiveEllipsizeMode, paddingTop: baseTop, paddingRight: baseRight, paddingBottom: baseBottom, paddingLeft: baseLeft, hybridRef: hybridRef ? callback(hybridRef) : undefined, pointerEvents: "none", style: styles.overlay })));
98
106
  }
99
107
  const styles = StyleSheet.create({
100
108
  container: {
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { StyleSheet, Text, View } from 'react-native';
2
+ import { I18nManager, StyleSheet, Text, View } from 'react-native';
3
3
  function resolveText(text, children) {
4
4
  if (typeof text === 'string')
5
5
  return text;
@@ -38,6 +38,24 @@ export function StrokeText({ text, children, style, ...rest }) {
38
38
  const strokeColor = rest.strokeColor ?? 'transparent';
39
39
  const strokeWidth = Math.max(0, rest.strokeWidth ?? 0);
40
40
  const strokeInset = Math.ceil(strokeWidth) / 2;
41
+ const baseMarginTop = firstNumber(containerStyle.marginTop, containerStyle.marginVertical, containerStyle.margin) ?? 0;
42
+ const baseMarginRight = firstNumber(containerStyle.marginRight, I18nManager.isRTL ? containerStyle.marginStart : containerStyle.marginEnd, containerStyle.marginHorizontal, containerStyle.margin) ?? 0;
43
+ const baseMarginBottom = firstNumber(containerStyle.marginBottom, containerStyle.marginVertical, containerStyle.margin) ?? 0;
44
+ const baseMarginLeft = firstNumber(containerStyle.marginLeft, I18nManager.isRTL ? containerStyle.marginEnd : containerStyle.marginStart, containerStyle.marginHorizontal, containerStyle.margin) ?? 0;
45
+ const baseMarginStart = toNumber(containerStyle.marginStart);
46
+ const baseMarginEnd = toNumber(containerStyle.marginEnd);
47
+ const strokeInsetMarginStyle = strokeInset === 0
48
+ ? null
49
+ : {
50
+ marginTop: baseMarginTop - strokeInset,
51
+ marginRight: baseMarginRight - strokeInset,
52
+ marginBottom: baseMarginBottom - strokeInset,
53
+ marginLeft: baseMarginLeft - strokeInset,
54
+ ...(baseMarginStart == null
55
+ ? {}
56
+ : { marginStart: baseMarginStart - strokeInset }),
57
+ ...(baseMarginEnd == null ? {} : { marginEnd: baseMarginEnd - strokeInset }),
58
+ };
41
59
  const baseTop = firstNumber(rest.paddingTop, stylePaddingTop, rest.paddingVertical, stylePaddingVertical, rest.padding, stylePadding) ?? 0;
42
60
  const baseRight = firstNumber(rest.paddingRight, stylePaddingRight, rest.paddingHorizontal, stylePaddingHorizontal, rest.padding, stylePadding) ?? 0;
43
61
  const baseBottom = firstNumber(rest.paddingBottom, stylePaddingBottom, rest.paddingVertical, stylePaddingVertical, rest.padding, stylePadding) ?? 0;
@@ -46,15 +64,17 @@ export function StrokeText({ text, children, style, ...rest }) {
46
64
  ? rest.numberOfLines
47
65
  : undefined;
48
66
  const effectiveEllipsizeMode = effectiveNumberOfLines == null ? undefined : rest.ellipsizeMode ?? 'tail';
49
- return (React.createElement(View, { style: [styles.container, containerStyle] },
67
+ return (React.createElement(View, { style: [styles.container, containerStyle, strokeInsetMarginStyle] },
50
68
  React.createElement(Text, { accessible: false, pointerEvents: "none", numberOfLines: effectiveNumberOfLines, ellipsizeMode: effectiveEllipsizeMode, style: [
51
69
  textStyle,
52
70
  {
53
- paddingTop: baseTop,
54
- paddingRight: baseRight,
55
- paddingBottom: baseBottom,
56
- paddingLeft: baseLeft,
71
+ paddingTop: baseTop + strokeInset,
72
+ paddingRight: baseRight + strokeInset,
73
+ paddingBottom: baseBottom + strokeInset,
74
+ paddingLeft: baseLeft + strokeInset,
57
75
  },
76
+ strokeInset === 0 ? null : { maxWidth: 'none' },
77
+ strokeInset === 0 ? null : { boxSizing: 'content-box' },
58
78
  styles.hiddenText,
59
79
  ] }, resolvedText),
60
80
  React.createElement(Text, { pointerEvents: "none", numberOfLines: effectiveNumberOfLines, ellipsizeMode: effectiveEllipsizeMode, style: [
@@ -83,14 +103,6 @@ export function StrokeText({ text, children, style, ...rest }) {
83
103
  }
84
104
  : null,
85
105
  styles.overlay,
86
- strokeInset === 0
87
- ? null
88
- : {
89
- top: -strokeInset,
90
- right: -strokeInset,
91
- bottom: -strokeInset,
92
- left: -strokeInset,
93
- },
94
106
  ] }, resolvedText)));
95
107
  }
96
108
  const styles = StyleSheet.create({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@whetware/react-native-stroke-text",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Stroke/outline text for React Native (New Architecture) on iOS, Android, and Web.",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -28,19 +28,6 @@
28
28
  "CHANGELOG.md",
29
29
  "README.md"
30
30
  ],
31
- "scripts": {
32
- "typecheck": "tsc --noEmit",
33
- "clean": "rm -rf android/build node_modules/**/android/build lib",
34
- "lint": "eslint \"**/*.{js,ts,tsx}\" --fix",
35
- "lint-ci": "eslint \"**/*.{js,ts,tsx}\"",
36
- "typescript": "tsc",
37
- "prepare": "npm run prepack",
38
- "prepack": "tsc",
39
- "changeset": "changeset",
40
- "version-packages": "changeset version",
41
- "release": "changeset publish",
42
- "specs": "tsc --noEmit false && nitrogen --logLevel=\"debug\""
43
- },
44
31
  "keywords": [
45
32
  "react-native",
46
33
  "nitro",
@@ -120,5 +107,16 @@
120
107
  "trailingComma": "es5",
121
108
  "useTabs": false,
122
109
  "semi": false
110
+ },
111
+ "scripts": {
112
+ "typecheck": "tsc --noEmit",
113
+ "clean": "rm -rf android/build node_modules/**/android/build lib",
114
+ "lint": "eslint \"**/*.{js,ts,tsx}\" --fix",
115
+ "lint-ci": "eslint \"**/*.{js,ts,tsx}\"",
116
+ "typescript": "tsc",
117
+ "changeset": "changeset",
118
+ "version-packages": "changeset version",
119
+ "release": "changeset publish",
120
+ "specs": "tsc --noEmit false && nitrogen --logLevel=\"debug\""
123
121
  }
124
- }
122
+ }