@studiokico/react-native-stroke-text 0.1.0 → 0.1.1
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/android/src/main/java/com/studiokico/stroketext/StrokeTextView.kt +7 -6
- package/ios/RNStrokeTextView.mm +0 -3
- package/ios/StrokeTextView.swift +28 -4
- package/lib/module/index.js +69 -2
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +5 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.tsx +86 -2
|
@@ -57,7 +57,9 @@ class StrokeTextView(context: Context) : View(context) {
|
|
|
57
57
|
private var textLayout: StaticLayout? = null // 여기가 핵심: 텍스트 레이아웃 엔진
|
|
58
58
|
|
|
59
59
|
private fun createLayout(width: Int): StaticLayout? {
|
|
60
|
-
if (text.isEmpty()
|
|
60
|
+
if (text.isEmpty()) return null
|
|
61
|
+
|
|
62
|
+
val validWidth = if (width > 0) width else resources.displayMetrics.widthPixels
|
|
61
63
|
|
|
62
64
|
val scaledFontSize = getScaledSize(fontSize)
|
|
63
65
|
textPaint.textSize = scaledFontSize
|
|
@@ -69,7 +71,7 @@ class StrokeTextView(context: Context) : View(context) {
|
|
|
69
71
|
else -> Layout.Alignment.ALIGN_NORMAL
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
val builder = StaticLayout.Builder.obtain(text, 0, text.length, textPaint,
|
|
74
|
+
val builder = StaticLayout.Builder.obtain(text, 0, text.length, textPaint, validWidth)
|
|
73
75
|
.setAlignment(alignment)
|
|
74
76
|
.setLineSpacing(0f, 1f)
|
|
75
77
|
.setIncludePad(true)
|
|
@@ -87,13 +89,12 @@ class StrokeTextView(context: Context) : View(context) {
|
|
|
87
89
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
88
90
|
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
|
|
89
91
|
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
|
|
90
|
-
|
|
91
92
|
val availableWidth = if (customWidth > 0) {
|
|
92
93
|
getScaledSize(customWidth).toInt()
|
|
93
|
-
} else if (
|
|
94
|
+
} else if (widthSize > 0) {
|
|
94
95
|
widthSize
|
|
95
96
|
} else {
|
|
96
|
-
resources.displayMetrics.widthPixels
|
|
97
|
+
resources.displayMetrics.widthPixels
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
val scaledStrokeWidth = getScaledSize(strokeWidth)
|
|
@@ -120,7 +121,7 @@ class StrokeTextView(context: Context) : View(context) {
|
|
|
120
121
|
}
|
|
121
122
|
finalWidth = (ceil(maxLineWidth) + paddingLeft + paddingRight + (scaledStrokeWidth * 2)).toInt()
|
|
122
123
|
|
|
123
|
-
if (widthMode == MeasureSpec.AT_MOST) {
|
|
124
|
+
if (widthMode == MeasureSpec.AT_MOST && widthSize > 0) {
|
|
124
125
|
finalWidth = minOf(finalWidth, widthSize)
|
|
125
126
|
}
|
|
126
127
|
}
|
package/ios/RNStrokeTextView.mm
CHANGED
|
@@ -8,9 +8,6 @@
|
|
|
8
8
|
#import <React/RCTFabricComponentsPlugins.h>
|
|
9
9
|
#import <React/RCTConversions.h>
|
|
10
10
|
|
|
11
|
-
// 2. Swift 클래스 import (프로젝트 설정에 따라 이름이 다를 수 있음)
|
|
12
|
-
// 라이브러리 개발 중이라면 보통 "라이브러리이름-Swift.h" 입니다.
|
|
13
|
-
// 예: react-native-stroke-text -> react_native_stroke_text-Swift.h
|
|
14
11
|
#if __has_include("StrokeText-Swift.h")
|
|
15
12
|
#import "StrokeText-Swift.h"
|
|
16
13
|
#else
|
package/ios/StrokeTextView.swift
CHANGED
|
@@ -31,6 +31,21 @@ public class StrokeTextView: UILabel {
|
|
|
31
31
|
self.numberOfLines = 0
|
|
32
32
|
self.clipsToBounds = false
|
|
33
33
|
self.backgroundColor = .clear
|
|
34
|
+
|
|
35
|
+
self.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
36
|
+
self.setContentCompressionResistancePriority(.required, for: .vertical)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public override func layoutSubviews() {
|
|
40
|
+
super.layoutSubviews()
|
|
41
|
+
|
|
42
|
+
if customWidth > 0 {
|
|
43
|
+
self.preferredMaxLayoutWidth = customWidth
|
|
44
|
+
} else {
|
|
45
|
+
if self.bounds.width > 0 {
|
|
46
|
+
self.preferredMaxLayoutWidth = self.bounds.width
|
|
47
|
+
}
|
|
48
|
+
}
|
|
34
49
|
}
|
|
35
50
|
|
|
36
51
|
public override func drawText(in rect: CGRect) {
|
|
@@ -40,18 +55,23 @@ public class StrokeTextView: UILabel {
|
|
|
40
55
|
let context = UIGraphicsGetCurrentContext()
|
|
41
56
|
context?.saveGState()
|
|
42
57
|
|
|
58
|
+
let height = self.textRect(forBounds: rect, limitedToNumberOfLines: self.numberOfLines).height
|
|
59
|
+
let topAlignedRect = CGRect(x: rect.origin.x, y: rect.origin.y, width: rect.width, height: height)
|
|
60
|
+
|
|
43
61
|
if strokeWidth > 0 {
|
|
44
62
|
context?.setLineWidth(strokeWidth)
|
|
45
63
|
context?.setLineJoin(.round)
|
|
46
64
|
context?.setTextDrawingMode(.stroke)
|
|
47
65
|
self.textColor = strokeColor
|
|
48
|
-
|
|
66
|
+
|
|
67
|
+
super.drawText(in: topAlignedRect)
|
|
49
68
|
}
|
|
50
69
|
|
|
51
70
|
context?.setTextDrawingMode(.fill)
|
|
52
71
|
self.textColor = originalTextColor
|
|
53
72
|
self.shadowOffset = CGSize.zero
|
|
54
|
-
|
|
73
|
+
|
|
74
|
+
super.drawText(in: topAlignedRect)
|
|
55
75
|
|
|
56
76
|
self.shadowOffset = shadowOffset
|
|
57
77
|
context?.restoreGState()
|
|
@@ -59,7 +79,11 @@ public class StrokeTextView: UILabel {
|
|
|
59
79
|
|
|
60
80
|
public override var intrinsicContentSize: CGSize {
|
|
61
81
|
let size = super.intrinsicContentSize
|
|
62
|
-
let extra = strokeWidth
|
|
63
|
-
|
|
82
|
+
let extra = strokeWidth * 2
|
|
83
|
+
|
|
84
|
+
let newWidth = size.width + extra
|
|
85
|
+
let newHeight = size.height + extra
|
|
86
|
+
|
|
87
|
+
return CGSize(width: newWidth, height: newHeight)
|
|
64
88
|
}
|
|
65
89
|
}
|
package/lib/module/index.js
CHANGED
|
@@ -1,5 +1,72 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { StyleSheet, Text, View } from 'react-native';
|
|
5
|
+
import StrokeTextViewNativeComponent from './StrokeTextViewNativeComponent';
|
|
6
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
+
export const StrokeText = props => {
|
|
8
|
+
const {
|
|
9
|
+
style,
|
|
10
|
+
text,
|
|
11
|
+
fontSize = 14,
|
|
12
|
+
color,
|
|
13
|
+
strokeColor,
|
|
14
|
+
strokeWidth = 0,
|
|
15
|
+
fontFamily,
|
|
16
|
+
align = 'left',
|
|
17
|
+
numberOfLines,
|
|
18
|
+
ellipsis,
|
|
19
|
+
width,
|
|
20
|
+
...rest
|
|
21
|
+
} = props;
|
|
22
|
+
const ghostTextStyle = {
|
|
23
|
+
fontSize: fontSize,
|
|
24
|
+
fontFamily: fontFamily,
|
|
25
|
+
textAlign: align || undefined,
|
|
26
|
+
width: width ? Number(width) : undefined,
|
|
27
|
+
margin: strokeWidth
|
|
28
|
+
};
|
|
29
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
30
|
+
style: [styles.container, style],
|
|
31
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
32
|
+
style: [ghostTextStyle, styles.ghostText],
|
|
33
|
+
numberOfLines: numberOfLines,
|
|
34
|
+
ellipsizeMode: ellipsis ? 'tail' : undefined,
|
|
35
|
+
children: text
|
|
36
|
+
}), /*#__PURE__*/_jsx(StrokeTextViewNativeComponent, {
|
|
37
|
+
style: styles.fill,
|
|
38
|
+
text: text,
|
|
39
|
+
fontSize: fontSize,
|
|
40
|
+
color: color,
|
|
41
|
+
strokeColor: strokeColor,
|
|
42
|
+
strokeWidth: strokeWidth,
|
|
43
|
+
fontFamily: fontFamily,
|
|
44
|
+
align: align,
|
|
45
|
+
numberOfLines: numberOfLines,
|
|
46
|
+
ellipsis: ellipsis,
|
|
47
|
+
width: width,
|
|
48
|
+
...rest
|
|
49
|
+
})]
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
const styles = StyleSheet.create({
|
|
53
|
+
container: {
|
|
54
|
+
alignItems: 'center',
|
|
55
|
+
justifyContent: 'center'
|
|
56
|
+
},
|
|
57
|
+
ghostText: {
|
|
58
|
+
opacity: 0,
|
|
59
|
+
zIndex: -1,
|
|
60
|
+
includeFontPadding: false,
|
|
61
|
+
textAlignVertical: 'center'
|
|
62
|
+
},
|
|
63
|
+
fill: {
|
|
64
|
+
position: 'absolute',
|
|
65
|
+
left: 0,
|
|
66
|
+
right: 0,
|
|
67
|
+
top: 0,
|
|
68
|
+
bottom: 0
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
export default StrokeText;
|
|
5
72
|
//# sourceMappingURL=index.js.map
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["
|
|
1
|
+
{"version":3,"names":["React","StyleSheet","Text","View","StrokeTextViewNativeComponent","jsx","_jsx","jsxs","_jsxs","StrokeText","props","style","text","fontSize","color","strokeColor","strokeWidth","fontFamily","align","numberOfLines","ellipsis","width","rest","ghostTextStyle","textAlign","undefined","Number","margin","styles","container","children","ghostText","ellipsizeMode","fill","create","alignItems","justifyContent","opacity","zIndex","includeFontPadding","textAlignVertical","position","left","right","top","bottom"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SACEC,UAAU,EACVC,IAAI,EACJC,IAAI,QAGC,cAAc;AACrB,OAAOC,6BAA6B,MAE7B,iCAAiC;AAAC,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAEzC,OAAO,MAAMC,UAAU,GAAIC,KAAkB,IAAwB;EACnE,MAAM;IACJC,KAAK;IACLC,IAAI;IACJC,QAAQ,GAAG,EAAE;IACbC,KAAK;IACLC,WAAW;IACXC,WAAW,GAAG,CAAC;IACfC,UAAU;IACVC,KAAK,GAAG,MAAM;IACdC,aAAa;IACbC,QAAQ;IACRC,KAAK;IACL,GAAGC;EACL,CAAC,GAAGZ,KAAK;EAET,MAAMa,cAAoC,GAAG;IAC3CV,QAAQ,EAAEA,QAAQ;IAClBI,UAAU,EAAEA,UAAU;IACtBO,SAAS,EAAEN,KAAK,IAAIO,SAAS;IAC7BJ,KAAK,EAAEA,KAAK,GAAGK,MAAM,CAACL,KAAK,CAAC,GAAGI,SAAS;IACxCE,MAAM,EAAEX;EACV,CAAC;EAED,oBACER,KAAA,CAACL,IAAI;IAACQ,KAAK,EAAE,CAACiB,MAAM,CAACC,SAAS,EAAElB,KAAK,CAAE;IAAAmB,QAAA,gBACrCxB,IAAA,CAACJ,IAAI;MACHS,KAAK,EAAE,CAACY,cAAc,EAAEK,MAAM,CAACG,SAAS,CAAE;MAC1CZ,aAAa,EAAEA,aAAc;MAC7Ba,aAAa,EAAEZ,QAAQ,GAAG,MAAM,GAAGK,SAAU;MAAAK,QAAA,EAE5ClB;IAAI,CACD,CAAC,eAEPN,IAAA,CAACF,6BAA6B;MAC5BO,KAAK,EAAEiB,MAAM,CAACK,IAAK;MACnBrB,IAAI,EAAEA,IAAK;MACXC,QAAQ,EAAEA,QAAS;MACnBC,KAAK,EAAEA,KAAM;MACbC,WAAW,EAAEA,WAAY;MACzBC,WAAW,EAAEA,WAAY;MACzBC,UAAU,EAAEA,UAAW;MACvBC,KAAK,EAAEA,KAAM;MACbC,aAAa,EAAEA,aAAc;MAC7BC,QAAQ,EAAEA,QAAS;MACnBC,KAAK,EAAEA,KAAM;MAAA,GACTC;IAAI,CACT,CAAC;EAAA,CACE,CAAC;AAEX,CAAC;AAED,MAAMM,MAAM,GAAG3B,UAAU,CAACiC,MAAM,CAAC;EAC/BL,SAAS,EAAE;IACTM,UAAU,EAAE,QAAQ;IACpBC,cAAc,EAAE;EAClB,CAAC;EACDL,SAAS,EAAE;IACTM,OAAO,EAAE,CAAC;IACVC,MAAM,EAAE,CAAC,CAAC;IACVC,kBAAkB,EAAE,KAAK;IACzBC,iBAAiB,EAAE;EACrB,CAAC;EACDP,IAAI,EAAE;IACJQ,QAAQ,EAAE,UAAU;IACpBC,IAAI,EAAE,CAAC;IACPC,KAAK,EAAE,CAAC;IACRC,GAAG,EAAE,CAAC;IACNC,MAAM,EAAE;EACV;AACF,CAAC,CAAC;AAGF,eAAepC,UAAU","ignoreList":[]}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type NativeProps } from './StrokeTextViewNativeComponent';
|
|
3
|
+
export declare const StrokeText: (props: NativeProps) => React.JSX.Element;
|
|
4
|
+
export type { NativeProps };
|
|
5
|
+
export default StrokeText;
|
|
3
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,OAAsC,EACpC,KAAK,WAAW,EACjB,MAAM,iCAAiC,CAAC;AAEzC,eAAO,MAAM,UAAU,GAAI,OAAO,WAAW,KAAG,KAAK,CAAC,GAAG,CAAC,OAkDzD,CAAC;AAsBF,YAAY,EAAE,WAAW,EAAE,CAAC;AAC5B,eAAe,UAAU,CAAC"}
|
package/package.json
CHANGED
package/src/index.tsx
CHANGED
|
@@ -1,2 +1,86 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
StyleSheet,
|
|
4
|
+
Text,
|
|
5
|
+
View,
|
|
6
|
+
type TextStyle,
|
|
7
|
+
type StyleProp,
|
|
8
|
+
} from 'react-native';
|
|
9
|
+
import StrokeTextViewNativeComponent, {
|
|
10
|
+
type NativeProps,
|
|
11
|
+
} from './StrokeTextViewNativeComponent';
|
|
12
|
+
|
|
13
|
+
export const StrokeText = (props: NativeProps): React.JSX.Element => {
|
|
14
|
+
const {
|
|
15
|
+
style,
|
|
16
|
+
text,
|
|
17
|
+
fontSize = 14,
|
|
18
|
+
color,
|
|
19
|
+
strokeColor,
|
|
20
|
+
strokeWidth = 0,
|
|
21
|
+
fontFamily,
|
|
22
|
+
align = 'left',
|
|
23
|
+
numberOfLines,
|
|
24
|
+
ellipsis,
|
|
25
|
+
width,
|
|
26
|
+
...rest
|
|
27
|
+
} = props;
|
|
28
|
+
|
|
29
|
+
const ghostTextStyle: StyleProp<TextStyle> = {
|
|
30
|
+
fontSize: fontSize,
|
|
31
|
+
fontFamily: fontFamily,
|
|
32
|
+
textAlign: align || undefined,
|
|
33
|
+
width: width ? Number(width) : undefined,
|
|
34
|
+
margin: strokeWidth,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<View style={[styles.container, style]}>
|
|
39
|
+
<Text
|
|
40
|
+
style={[ghostTextStyle, styles.ghostText]}
|
|
41
|
+
numberOfLines={numberOfLines}
|
|
42
|
+
ellipsizeMode={ellipsis ? 'tail' : undefined}
|
|
43
|
+
>
|
|
44
|
+
{text}
|
|
45
|
+
</Text>
|
|
46
|
+
|
|
47
|
+
<StrokeTextViewNativeComponent
|
|
48
|
+
style={styles.fill}
|
|
49
|
+
text={text}
|
|
50
|
+
fontSize={fontSize}
|
|
51
|
+
color={color}
|
|
52
|
+
strokeColor={strokeColor}
|
|
53
|
+
strokeWidth={strokeWidth}
|
|
54
|
+
fontFamily={fontFamily}
|
|
55
|
+
align={align}
|
|
56
|
+
numberOfLines={numberOfLines}
|
|
57
|
+
ellipsis={ellipsis}
|
|
58
|
+
width={width}
|
|
59
|
+
{...rest}
|
|
60
|
+
/>
|
|
61
|
+
</View>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const styles = StyleSheet.create({
|
|
66
|
+
container: {
|
|
67
|
+
alignItems: 'center',
|
|
68
|
+
justifyContent: 'center',
|
|
69
|
+
},
|
|
70
|
+
ghostText: {
|
|
71
|
+
opacity: 0,
|
|
72
|
+
zIndex: -1,
|
|
73
|
+
includeFontPadding: false,
|
|
74
|
+
textAlignVertical: 'center',
|
|
75
|
+
},
|
|
76
|
+
fill: {
|
|
77
|
+
position: 'absolute',
|
|
78
|
+
left: 0,
|
|
79
|
+
right: 0,
|
|
80
|
+
top: 0,
|
|
81
|
+
bottom: 0,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
export type { NativeProps };
|
|
86
|
+
export default StrokeText;
|