UNPKG

15.2 kBJavaScriptView Raw
1// Types
2import { getClosestPropertyValue } from './text-base-common';
3// Requires
4import { Font } from '../styling/font';
5import { TextBaseCommon, textProperty, formattedTextProperty, textAlignmentProperty, textDecorationProperty, textTransformProperty, textShadowProperty, letterSpacingProperty, lineHeightProperty, resetSymbol } from './text-base-common';
6import { Color } from '../../color';
7import { Span } from './span';
8import { colorProperty, fontInternalProperty, Length } from '../styling/style-properties';
9import { isString, isNullOrUndefined } from '../../utils/types';
10import { iOSNativeHelper } from '../../utils';
11import { Trace } from '../../trace';
12export * from './text-base-common';
13const majorVersion = iOSNativeHelper.MajorVersion;
14var UILabelClickHandlerImpl = /** @class */ (function (_super) {
15 __extends(UILabelClickHandlerImpl, _super);
16 function UILabelClickHandlerImpl() {
17 return _super !== null && _super.apply(this, arguments) || this;
18 }
19 UILabelClickHandlerImpl.initWithOwner = function (owner) {
20 var handler = UILabelClickHandlerImpl.new();
21 handler._owner = owner;
22 return handler;
23 };
24 UILabelClickHandlerImpl.prototype.linkTap = function (tapGesture) {
25 var owner = this._owner.get();
26 if (owner) {
27 // https://stackoverflow.com/a/35789589
28 var label = owner.nativeTextViewProtected;
29 var layoutManager = NSLayoutManager.alloc().init();
30 var textContainer = NSTextContainer.alloc().initWithSize(CGSizeZero);
31 var textStorage = NSTextStorage.alloc().initWithAttributedString(owner.nativeTextViewProtected['attributedText']);
32 layoutManager.addTextContainer(textContainer);
33 textStorage.addLayoutManager(layoutManager);
34 textContainer.lineFragmentPadding = 0;
35 textContainer.lineBreakMode = label.lineBreakMode;
36 textContainer.maximumNumberOfLines = label.numberOfLines;
37 var labelSize = label.bounds.size;
38 textContainer.size = labelSize;
39 var locationOfTouchInLabel = tapGesture.locationInView(label);
40 var textBoundingBox = layoutManager.usedRectForTextContainer(textContainer);
41 var textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
42 var locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x, locationOfTouchInLabel.y - textContainerOffset.y);
43 var indexOfCharacter = layoutManager.characterIndexForPointInTextContainerFractionOfDistanceBetweenInsertionPoints(locationOfTouchInTextContainer, textContainer, null);
44 var span = null;
45 // try to find the corresponding span using the spanRanges
46 for (var i = 0; i < owner._spanRanges.length; i++) {
47 var range = owner._spanRanges[i];
48 if (range.location <= indexOfCharacter && range.location + range.length > indexOfCharacter) {
49 if (owner.formattedText && owner.formattedText.spans.length > i) {
50 span = owner.formattedText.spans.getItem(i);
51 }
52 break;
53 }
54 }
55 if (span && span.tappable) {
56 // if the span is found and tappable emit the linkTap event
57 span._emit(Span.linkTapEvent);
58 }
59 }
60 };
61 UILabelClickHandlerImpl.ObjCExposedMethods = {
62 linkTap: { returns: interop.types.void, params: [interop.types.id] },
63 };
64 return UILabelClickHandlerImpl;
65}(NSObject));
66export class TextBase extends TextBaseCommon {
67 constructor() {
68 super(...arguments);
69 this._tappable = false;
70 }
71 initNativeView() {
72 super.initNativeView();
73 this._setTappableState(false);
74 }
75 _setTappableState(tappable) {
76 if (this._tappable !== tappable) {
77 this._tappable = tappable;
78 if (this._tappable) {
79 const tapHandler = UILabelClickHandlerImpl.initWithOwner(new WeakRef(this));
80 // associate handler with menuItem or it will get collected by JSC.
81 this.handler = tapHandler;
82 this._tapGestureRecognizer = UITapGestureRecognizer.alloc().initWithTargetAction(tapHandler, 'linkTap');
83 this.nativeViewProtected.userInteractionEnabled = true;
84 this.nativeViewProtected.addGestureRecognizer(this._tapGestureRecognizer);
85 }
86 else {
87 this.nativeViewProtected.userInteractionEnabled = false;
88 this.nativeViewProtected.removeGestureRecognizer(this._tapGestureRecognizer);
89 }
90 }
91 }
92 [textProperty.getDefault]() {
93 return resetSymbol;
94 }
95 [textProperty.setNative](value) {
96 const reset = value === resetSymbol;
97 if (!reset && this.formattedText) {
98 return;
99 }
100 this._setNativeText(reset);
101 this._requestLayoutOnTextChanged();
102 }
103 [formattedTextProperty.setNative](value) {
104 this._setNativeText();
105 this._setTappableState(isStringTappable(value));
106 textProperty.nativeValueChange(this, !value ? '' : value.toString());
107 this._requestLayoutOnTextChanged();
108 }
109 [colorProperty.getDefault]() {
110 const nativeView = this.nativeTextViewProtected;
111 if (nativeView instanceof UIButton) {
112 return nativeView.titleColorForState(0 /* Normal */);
113 }
114 else {
115 return nativeView.textColor;
116 }
117 }
118 [colorProperty.setNative](value) {
119 const color = value instanceof Color ? value.ios : value;
120 this._setColor(color);
121 }
122 [fontInternalProperty.getDefault]() {
123 let nativeView = this.nativeTextViewProtected;
124 nativeView = nativeView instanceof UIButton ? nativeView.titleLabel : nativeView;
125 return nativeView.font;
126 }
127 [fontInternalProperty.setNative](value) {
128 if (!(value instanceof Font) || !this.formattedText) {
129 let nativeView = this.nativeTextViewProtected;
130 nativeView = nativeView instanceof UIButton ? nativeView.titleLabel : nativeView;
131 nativeView.font = value instanceof Font ? value.getUIFont(nativeView.font) : value;
132 }
133 }
134 [textAlignmentProperty.setNative](value) {
135 const nativeView = this.nativeTextViewProtected;
136 switch (value) {
137 case 'initial':
138 case 'left':
139 nativeView.textAlignment = 0 /* Left */;
140 break;
141 case 'center':
142 nativeView.textAlignment = 1 /* Center */;
143 break;
144 case 'right':
145 nativeView.textAlignment = 2 /* Right */;
146 break;
147 case 'justify':
148 nativeView.textAlignment = 3 /* Justified */;
149 break;
150 }
151 }
152 [textDecorationProperty.setNative](value) {
153 this._setNativeText();
154 }
155 [textTransformProperty.setNative](value) {
156 this._setNativeText();
157 }
158 [letterSpacingProperty.setNative](value) {
159 this._setNativeText();
160 }
161 [lineHeightProperty.setNative](value) {
162 this._setNativeText();
163 }
164 [textShadowProperty.setNative](value) {
165 this._setShadow(value);
166 }
167 _setColor(color) {
168 if (this.nativeTextViewProtected instanceof UIButton) {
169 this.nativeTextViewProtected.setTitleColorForState(color, 0 /* Normal */);
170 this.nativeTextViewProtected.titleLabel.textColor = color;
171 }
172 else {
173 this.nativeTextViewProtected.textColor = color;
174 }
175 }
176 _setNativeText(reset = false) {
177 var _a;
178 if (reset) {
179 const nativeView = this.nativeTextViewProtected;
180 if (nativeView instanceof UIButton) {
181 // Clear attributedText or title won't be affected.
182 nativeView.setAttributedTitleForState(null, 0 /* Normal */);
183 nativeView.setTitleForState(null, 0 /* Normal */);
184 }
185 else {
186 // Clear attributedText or text won't be affected.
187 nativeView.attributedText = null;
188 nativeView.text = null;
189 }
190 return;
191 }
192 if (this.formattedText) {
193 this.nativeTextViewProtected.nativeScriptSetFormattedTextDecorationAndTransform(this.getFormattedStringDetails(this.formattedText));
194 }
195 else {
196 // console.log('setTextDecorationAndTransform...')
197 const text = getTransformedText(isNullOrUndefined(this.text) ? '' : `${this.text}`, this.textTransform);
198 this.nativeTextViewProtected.nativeScriptSetTextDecorationAndTransformTextDecorationLetterSpacingLineHeight(text, this.style.textDecoration || '', this.style.letterSpacing !== 0 ? this.style.letterSpacing : 0, this.style.lineHeight ? this.style.lineHeight : 0);
199 if (!((_a = this.style) === null || _a === void 0 ? void 0 : _a.color) && majorVersion >= 13 && UIColor.labelColor) {
200 this._setColor(UIColor.labelColor);
201 }
202 }
203 }
204 createFormattedTextNative(value) {
205 return NativeScriptUtils.createMutableStringWithDetails(this.getFormattedStringDetails(value));
206 }
207 getFormattedStringDetails(formattedString) {
208 const details = {
209 spans: [],
210 };
211 this._spanRanges = [];
212 if (formattedString && formattedString.parent) {
213 for (let i = 0, spanStart = 0, length = formattedString.spans.length; i < length; i++) {
214 const span = formattedString.spans.getItem(i);
215 const text = span.text;
216 const textTransform = formattedString.parent.textTransform;
217 let spanText = isNullOrUndefined(text) ? '' : `${text}`;
218 if (textTransform !== 'none' && textTransform !== 'initial') {
219 spanText = getTransformedText(spanText, textTransform);
220 }
221 details.spans.push(this.createMutableStringDetails(span, spanText, spanStart));
222 this._spanRanges.push({
223 location: spanStart,
224 length: spanText.length,
225 });
226 spanStart += spanText.length;
227 }
228 }
229 return details;
230 }
231 createMutableStringDetails(span, text, index) {
232 const font = new Font(span.style.fontFamily, span.style.fontSize, span.style.fontStyle, span.style.fontWeight);
233 const iosFont = font.getUIFont(this.nativeTextViewProtected.font);
234 const backgroundColor = (span.style.backgroundColor || span.parent.backgroundColor || span.parent.parent.backgroundColor);
235 return {
236 text,
237 iosFont,
238 color: span.color ? span.color.ios : null,
239 backgroundColor: backgroundColor ? backgroundColor.ios : null,
240 textDecoration: getClosestPropertyValue(textDecorationProperty, span),
241 letterSpacing: this.letterSpacing || 0,
242 lineHeight: this.lineHeight || 0,
243 baselineOffset: this.getBaselineOffset(iosFont, span.style.verticalAlignment),
244 index,
245 };
246 }
247 createMutableStringForSpan(span, text) {
248 const details = this.createMutableStringDetails(span, text);
249 return NativeScriptUtils.createMutableStringForSpanFontColorBackgroundColorTextDecorationBaselineOffset(details.text, details.iosFont, details.color, details.backgroundColor, details.textDecoration, details.baselineOffset);
250 }
251 getBaselineOffset(font, align) {
252 if (!align || ['stretch', 'baseline'].includes(align)) {
253 return 0;
254 }
255 if (align === 'top') {
256 return -this.fontSize - font.descender - font.ascender - font.leading / 2;
257 }
258 if (align === 'bottom') {
259 return font.descender + font.leading / 2;
260 }
261 if (align === 'text-top') {
262 return -this.fontSize - font.descender - font.ascender;
263 }
264 if (align === 'text-bottom') {
265 return font.descender;
266 }
267 if (align === 'middle') {
268 return (font.descender - font.ascender) / 2 - font.descender;
269 }
270 if (align === 'sup') {
271 return -this.fontSize * 0.4;
272 }
273 if (align === 'sub') {
274 return (font.descender - font.ascender) * 0.4;
275 }
276 }
277 _setShadow(value) {
278 var _a, _b;
279 const layer = iOSNativeHelper.getShadowLayer(this.nativeTextViewProtected, 'ns-text-shadow');
280 if (!layer) {
281 Trace.write('text-shadow not applied, no layer.', Trace.categories.Style, Trace.messageType.info);
282 return;
283 }
284 if (isNullOrUndefined(value)) {
285 // clear the text shadow
286 layer.shadowOpacity = 0;
287 layer.shadowRadius = 0;
288 layer.shadowColor = UIColor.clearColor;
289 layer.shadowOffset = CGSizeMake(0, 0);
290 return;
291 }
292 // shadow opacity is handled on the shadow's color instance
293 layer.shadowOpacity = ((_a = value.color) === null || _a === void 0 ? void 0 : _a.a) ? ((_b = value.color) === null || _b === void 0 ? void 0 : _b.a) / 255 : 1;
294 layer.shadowColor = value.color.ios.CGColor;
295 layer.shadowRadius = Length.toDevicePixels(value.blurRadius, 0.0);
296 // prettier-ignore
297 layer.shadowOffset = CGSizeMake(Length.toDevicePixels(value.offsetX, 0.0), Length.toDevicePixels(value.offsetY, 0.0));
298 layer.masksToBounds = false;
299 // NOTE: generally should not need shouldRasterize
300 // however for various detailed animation work which involves text-shadow applicable layers, we may want to give users the control of enabling this with text-shadow
301 // if (!(this.nativeTextViewProtected instanceof UITextView)) {
302 // layer.shouldRasterize = true;
303 // }
304 }
305}
306export function getTransformedText(text, textTransform) {
307 if (!text || !isString(text)) {
308 return '';
309 }
310 switch (textTransform) {
311 case 'uppercase':
312 return NSStringFromNSAttributedString(text).uppercaseString;
313 case 'lowercase':
314 return NSStringFromNSAttributedString(text).lowercaseString;
315 case 'capitalize':
316 return NSStringFromNSAttributedString(text).capitalizedString;
317 default:
318 return text;
319 }
320}
321function NSStringFromNSAttributedString(source) {
322 return NSString.stringWithString((source instanceof NSAttributedString && source.string) || source);
323}
324function isStringTappable(formattedString) {
325 if (!formattedString) {
326 return false;
327 }
328 for (let i = 0, length = formattedString.spans.length; i < length; i++) {
329 const span = formattedString.spans.getItem(i);
330 if (span.tappable) {
331 return true;
332 }
333 }
334 return false;
335}
336//# sourceMappingURL=index.ios.js.map
\No newline at end of file